boards: x86: add qemu_x86_virt to test running in virtual space
This adds a new qemu_x86_virt board where code and data are mapped in virtual address space and is actually executing within virtual address space. Signed-off-by: Daniel Leung <daniel.leung@intel.com>
This commit is contained in:
parent
9de70a78fe
commit
7a1766d3b6
6 changed files with 243 additions and 0 deletions
|
@ -8,3 +8,12 @@ set_property(GLOBAL APPEND PROPERTY extra_post_build_commands
|
||||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if("${BOARD}" STREQUAL "qemu_x86_virt")
|
||||||
|
set_property(GLOBAL APPEND PROPERTY extra_post_build_commands
|
||||||
|
COMMAND ${PYTHON_EXECUTABLE} ${BOARD_DIR}/scripts/update_elf_load_addr.py
|
||||||
|
--kernel ${PROJECT_BINARY_DIR}/${CONFIG_KERNEL_BIN_NAME}.elf
|
||||||
|
--output ${PROJECT_BINARY_DIR}/${BOARD}.elf
|
||||||
|
$<$<BOOL:${CMAKE_VERBOSE_MAKEFILE}>:--verbose>
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
|
@ -65,6 +65,10 @@ if(NOT CONFIG_ACPI)
|
||||||
list(APPEND QEMU_FLAGS_${ARCH} -no-acpi)
|
list(APPEND QEMU_FLAGS_${ARCH} -no-acpi)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if("${BOARD}" STREQUAL "qemu_x86_virt")
|
||||||
|
set(QEMU_KERNEL_OPTION "-kernel;${ZEPHYR_BINARY_DIR}/${BOARD}.elf")
|
||||||
|
endif()
|
||||||
|
|
||||||
# TODO: Support debug
|
# TODO: Support debug
|
||||||
# board_set_debugger_ifnset(qemu)
|
# board_set_debugger_ifnset(qemu)
|
||||||
# debugserver: QEMU_EXTRA_FLAGS += -s -S
|
# debugserver: QEMU_EXTRA_FLAGS += -s -S
|
||||||
|
|
7
boards/x86/qemu_x86/qemu_x86_virt.dts
Normal file
7
boards/x86/qemu_x86/qemu_x86_virt.dts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 Intel Corporation.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu_x86.dts"
|
13
boards/x86/qemu_x86/qemu_x86_virt.yaml
Normal file
13
boards/x86/qemu_x86/qemu_x86_virt.yaml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
identifier: qemu_x86_virt
|
||||||
|
name: QEMU Emulation for X86 (Run in Virtual Address Space)
|
||||||
|
type: qemu
|
||||||
|
arch: x86
|
||||||
|
simulation: qemu
|
||||||
|
toolchain:
|
||||||
|
- zephyr
|
||||||
|
- xtools
|
||||||
|
testing:
|
||||||
|
default: true
|
||||||
|
only_tags:
|
||||||
|
- kernel
|
||||||
|
- userspace
|
25
boards/x86/qemu_x86/qemu_x86_virt_defconfig
Normal file
25
boards/x86/qemu_x86/qemu_x86_virt_defconfig
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
CONFIG_SOC_IA32=y
|
||||||
|
CONFIG_BOARD_QEMU_X86=y
|
||||||
|
CONFIG_HPET_TIMER=y
|
||||||
|
CONFIG_PIC_DISABLE=y
|
||||||
|
CONFIG_LOAPIC=y
|
||||||
|
CONFIG_CONSOLE=y
|
||||||
|
CONFIG_SERIAL=y
|
||||||
|
CONFIG_UART_NS16550=y
|
||||||
|
CONFIG_UART_CONSOLE=y
|
||||||
|
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=25000000
|
||||||
|
CONFIG_TEST_RANDOM_GENERATOR=y
|
||||||
|
CONFIG_X86_MMU=y
|
||||||
|
CONFIG_DEBUG_INFO=y
|
||||||
|
CONFIG_SCHED_SCALABLE=y
|
||||||
|
CONFIG_WAITQ_SCALABLE=y
|
||||||
|
CONFIG_X86_VERY_EARLY_CONSOLE=y
|
||||||
|
CONFIG_QEMU_ICOUNT_SHIFT=5
|
||||||
|
|
||||||
|
CONFIG_SRAM_OFFSET=0x100000
|
||||||
|
CONFIG_KERNEL_VM_SIZE=0x400000
|
||||||
|
CONFIG_KERNEL_VM_BASE=0x40000000
|
||||||
|
CONFIG_KERNEL_VM_OFFSET=0x100000
|
||||||
|
CONFIG_KERNEL_LINK_IN_VIRT=y
|
185
boards/x86/qemu_x86/scripts/update_elf_load_addr.py
Normal file
185
boards/x86/qemu_x86/scripts/update_elf_load_addr.py
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# Copyright (c) 2021 Intel Corporation
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
"""Update ELF Load Address
|
||||||
|
|
||||||
|
This updates the entry and physical load addresses in ELF file
|
||||||
|
so that QEMU is able to load and run the Zephyr kernel even
|
||||||
|
though the kernel is linked in virtual address space.
|
||||||
|
|
||||||
|
When the Zephyr kernel is linked in virtual address space,
|
||||||
|
the ELF file only contains virtual addresses which may result
|
||||||
|
in QEMU loading code and data into non-existent physical memory
|
||||||
|
if both physical and virtual address space do not start at
|
||||||
|
the same address. This script modifies the physical addresses
|
||||||
|
of the load segments so QEMU will place code and data in
|
||||||
|
physical memory. This also updates the entry address to physical
|
||||||
|
address so QEMU can jump to it to start the Zephyr kernel.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import struct
|
||||||
|
|
||||||
|
from ctypes import create_string_buffer
|
||||||
|
from elftools.elf.elffile import ELFFile
|
||||||
|
from elftools.elf.sections import SymbolTableSection
|
||||||
|
|
||||||
|
|
||||||
|
# ELF Header, load entry offset
|
||||||
|
ELF_HDR_ENTRY_OFFSET = 0x18
|
||||||
|
|
||||||
|
|
||||||
|
def log(text):
|
||||||
|
"""Output log text if --verbose is used"""
|
||||||
|
if args.verbose:
|
||||||
|
sys.stdout.write(os.path.basename(sys.argv[0]) + ": " + text + "\n")
|
||||||
|
|
||||||
|
|
||||||
|
def error(text):
|
||||||
|
"""Output error message"""
|
||||||
|
sys.exit(os.path.basename(sys.argv[0]) + ": " + text)
|
||||||
|
|
||||||
|
|
||||||
|
def extract_all_symbols_from_elf(obj):
|
||||||
|
"""Get all symbols from the ELF file"""
|
||||||
|
all_syms = {}
|
||||||
|
for section in obj.iter_sections():
|
||||||
|
if isinstance(section, SymbolTableSection):
|
||||||
|
for sym in section.iter_symbols():
|
||||||
|
all_syms[sym.name] = sym.entry.st_value
|
||||||
|
|
||||||
|
if len(all_syms) == 0:
|
||||||
|
raise LookupError("Could not find symbol table")
|
||||||
|
|
||||||
|
return all_syms
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
"""Parse command line arguments"""
|
||||||
|
global args
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
|
||||||
|
parser.add_argument("-k", "--kernel", required=True,
|
||||||
|
help="Zephyr ELF binary")
|
||||||
|
parser.add_argument("-o", "--output", required=True,
|
||||||
|
help="Output path")
|
||||||
|
parser.add_argument("-v", "--verbose", action="store_true",
|
||||||
|
help="Print extra debugging information")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Main function"""
|
||||||
|
global args
|
||||||
|
parse_args()
|
||||||
|
|
||||||
|
if not os.path.exists(args.kernel):
|
||||||
|
error("{0} does not exist.".format(args.kernel))
|
||||||
|
|
||||||
|
elf_fd = open(args.kernel, "rb")
|
||||||
|
|
||||||
|
# Create a modifiable byte stream
|
||||||
|
raw_elf = elf_fd.read()
|
||||||
|
output = create_string_buffer(raw_elf)
|
||||||
|
|
||||||
|
elf = ELFFile(elf_fd)
|
||||||
|
|
||||||
|
if not elf.has_dwarf_info():
|
||||||
|
error("ELF file has no DWARF information")
|
||||||
|
|
||||||
|
if elf.num_segments() == 0:
|
||||||
|
error("ELF file has no program header table")
|
||||||
|
|
||||||
|
syms = extract_all_symbols_from_elf(elf)
|
||||||
|
|
||||||
|
vm_base = syms["CONFIG_KERNEL_VM_BASE"]
|
||||||
|
vm_size = syms["CONFIG_KERNEL_VM_SIZE"]
|
||||||
|
|
||||||
|
sram_base = syms["CONFIG_SRAM_BASE_ADDRESS"]
|
||||||
|
sram_size = syms["CONFIG_SRAM_SIZE"] * 1024
|
||||||
|
|
||||||
|
vm_offset = syms["CONFIG_KERNEL_VM_OFFSET"]
|
||||||
|
sram_offset = syms.get("CONFIG_SRAM_OFFSET", 0)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Calculate virtual-to-physical address translation
|
||||||
|
#
|
||||||
|
virt_to_phys_offset = (sram_base + sram_offset) - (vm_base + vm_offset)
|
||||||
|
|
||||||
|
log("Virtual address space: 0x%x - 0x%x size 0x%x (offset 0x%x)" %
|
||||||
|
(vm_base, vm_base + vm_size, vm_size, vm_offset))
|
||||||
|
log("Physical address space: 0x%x - 0x%x size 0x%x (offset 0x%x)" %
|
||||||
|
(sram_base, sram_base + sram_size, sram_size, sram_offset))
|
||||||
|
|
||||||
|
#
|
||||||
|
# Update the entry address in header
|
||||||
|
#
|
||||||
|
if elf.elfclass == 32:
|
||||||
|
load_entry_type = "I"
|
||||||
|
else:
|
||||||
|
load_entry_type = "Q"
|
||||||
|
|
||||||
|
entry_virt = struct.unpack_from(load_entry_type, output, ELF_HDR_ENTRY_OFFSET)[0]
|
||||||
|
entry_phys = entry_virt + virt_to_phys_offset
|
||||||
|
struct.pack_into(load_entry_type, output, ELF_HDR_ENTRY_OFFSET, entry_phys)
|
||||||
|
|
||||||
|
log("Entry Address: 0x%x -> 0x%x" % (entry_virt, entry_phys))
|
||||||
|
|
||||||
|
#
|
||||||
|
# Update load address in program header segments
|
||||||
|
#
|
||||||
|
|
||||||
|
# Program header segment offset from beginning of file
|
||||||
|
ph_off = elf.header['e_phoff']
|
||||||
|
|
||||||
|
# ph_seg_type: segment type and other fields before virtual address
|
||||||
|
# ph_seg_addr: virtual and phyiscal addresses
|
||||||
|
# ph_seg_whole: whole segment
|
||||||
|
if elf.elfclass == 32:
|
||||||
|
ph_seg_type = "II"
|
||||||
|
ph_seg_addr = "II"
|
||||||
|
ph_seg_whole = "IIIIIIII"
|
||||||
|
else:
|
||||||
|
ph_seg_type = "IIQ"
|
||||||
|
ph_seg_addr = "QQ"
|
||||||
|
ph_seg_whole = "IIQQQQQQ"
|
||||||
|
|
||||||
|
# Go through all segments
|
||||||
|
for ph_idx in range(elf.num_segments()):
|
||||||
|
seg_off = ph_off + struct.calcsize(ph_seg_whole) * ph_idx
|
||||||
|
|
||||||
|
seg_type, _ = struct.unpack_from(ph_seg_type, output, seg_off)
|
||||||
|
|
||||||
|
# Only process LOAD segments
|
||||||
|
if seg_type != 0x01:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Add offset to get to the addresses
|
||||||
|
addr_off = seg_off + struct.calcsize(ph_seg_type)
|
||||||
|
|
||||||
|
# Grab virtual and physical addresses
|
||||||
|
seg_vaddr, seg_paddr = struct.unpack_from(ph_seg_addr, output, addr_off)
|
||||||
|
|
||||||
|
# Apply virt-to-phys offset so it will load into
|
||||||
|
# physical address
|
||||||
|
seg_paddr_new = seg_vaddr + virt_to_phys_offset
|
||||||
|
|
||||||
|
log("Segment %d: physical address 0x%x -> 0x%x" % (ph_idx, seg_paddr, seg_paddr_new))
|
||||||
|
|
||||||
|
# Put the addresses back
|
||||||
|
struct.pack_into(ph_seg_addr, output, addr_off, seg_vaddr, seg_paddr_new)
|
||||||
|
|
||||||
|
out_fd = open(args.output, "wb")
|
||||||
|
out_fd.write(output)
|
||||||
|
out_fd.close()
|
||||||
|
|
||||||
|
elf_fd.close()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Loading…
Add table
Add a link
Reference in a new issue