From 1c142c76d4bf17cbeae508cb29dade33ed32e865 Mon Sep 17 00:00:00 2001 From: Ivan Velickovic Date: Mon, 22 Jul 2024 16:16:15 +1000 Subject: [PATCH 01/10] build_sdk.py: fix style Signed-off-by: Ivan Velickovic --- build_sdk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_sdk.py b/build_sdk.py index a270689a8..7db9f1f65 100644 --- a/build_sdk.py +++ b/build_sdk.py @@ -355,7 +355,7 @@ def build_elf_component( build_dir.mkdir(exist_ok=True, parents=True) defines_str = " ".join(f"{k}={v}" for k, v in defines) r = system( - f"BOARD={board.name} BUILD_DIR={build_dir.absolute()} GCC_CPU={board.gcc_cpu} SEL4_SDK={sel4_dir.absolute()} {defines_str} make -C {component_name}" + f"BOARD={board.name} BUILD_DIR={build_dir.absolute()} GCC_CPU={board.gcc_cpu} SEL4_SDK={sel4_dir.absolute()} {defines_str} make -C {component_name}" ) if r != 0: raise Exception( From 23967ff0b5b7194881043bfecf7f799b9ba77385 Mon Sep 17 00:00:00 2001 From: Ivan Velickovic Date: Fri, 2 Aug 2024 10:17:10 +1000 Subject: [PATCH 02/10] Add RISC-V support Signed-off-by: Ivan Velickovic --- build_sdk.py | 99 +- docs/manual.md | 14 +- libmicrokit/Makefile | 37 +- libmicrokit/microkit.ld | 6 + libmicrokit/src/{ => aarch64}/crt0.s | 0 libmicrokit/src/riscv/crt0.s | 20 + loader/Makefile | 44 +- loader/{loader.ld => aarch64.ld} | 0 loader/riscv64.ld | 43 + loader/src/{ => aarch64}/crt0.s | 0 loader/src/{ => aarch64}/util64.S | 0 loader/src/loader.c | 78 +- loader/src/riscv/crt0.S | 132 +++ monitor/Makefile | 35 +- monitor/monitor.ld | 4 + monitor/src/{ => aarch64}/crt0.s | 0 monitor/src/main.c | 381 ++++++-- monitor/src/riscv/crt0.s | 22 + tool/microkit/src/elf.rs | 11 +- tool/microkit/src/lib.rs | 36 +- tool/microkit/src/loader.rs | 151 ++- tool/microkit/src/main.rs | 1298 ++++++++++++++++---------- tool/microkit/src/sdf.rs | 12 +- tool/microkit/src/sel4.rs | 592 +++++++++--- tool/microkit/src/util.rs | 9 + tool/microkit/tests/test.rs | 4 +- 26 files changed, 2208 insertions(+), 820 deletions(-) rename libmicrokit/src/{ => aarch64}/crt0.s (100%) create mode 100644 libmicrokit/src/riscv/crt0.s rename loader/{loader.ld => aarch64.ld} (100%) create mode 100644 loader/riscv64.ld rename loader/src/{ => aarch64}/crt0.s (100%) rename loader/src/{ => aarch64}/util64.S (100%) create mode 100644 loader/src/riscv/crt0.S rename monitor/src/{ => aarch64}/crt0.s (100%) create mode 100644 monitor/src/riscv/crt0.s diff --git a/build_sdk.py b/build_sdk.py index 7db9f1f65..9ff70eacb 100644 --- a/build_sdk.py +++ b/build_sdk.py @@ -20,8 +20,10 @@ from sys import executable from tarfile import open as tar_open, TarInfo import platform as host_platform +from enum import IntEnum +import json -from typing import Dict, Union, List, Tuple +from typing import Any, Dict, Union, List, Tuple, Optional NAME = "microkit" VERSION = "1.3.0" @@ -30,20 +32,39 @@ MICROKIT_EPOCH = 1616367257 -# Due to only supporting AArch64 (for now), we hard-code -# the prefix of the toolchain used to compile seL4. -TOOLCHAIN_PREFIX = "aarch64-none-elf-" +TOOLCHAIN_AARCH64 = "aarch64-none-elf-" +TOOLCHAIN_RISCV = "riscv64-unknown-elf-" KERNEL_CONFIG_TYPE = Union[bool, str] -KERNEL_OPTIONS = Dict[str, KERNEL_CONFIG_TYPE] +KERNEL_OPTIONS = Dict[str, Union[bool, str]] + + +class KernelArch(IntEnum): + AARCH64 = 1 + RISCV64 = 2 + + def is_riscv(self) -> bool: + return self == KernelArch.RISCV64 + + def is_arm(self) -> bool: + return self == KernelArch.AARCH64 + + def to_str(self) -> str: + if self == KernelArch.AARCH64: + return "aarch64" + elif self == KernelArch.RISCV64: + return "riscv64" + else: + raise Exception(f"Unsupported arch {self}") @dataclass class BoardInfo: name: str - gcc_cpu: str + arch: KernelArch + gcc_cpu: Optional[str] loader_link_address: int - kernel_options: KERNEL_CONFIG_TYPE + kernel_options: KERNEL_OPTIONS examples: Dict[str, Path] @@ -51,12 +72,13 @@ class BoardInfo: class ConfigInfo: name: str debug: bool - kernel_options: KERNEL_CONFIG_TYPE + kernel_options: KERNEL_OPTIONS SUPPORTED_BOARDS = ( BoardInfo( name="tqma8xqp1gb", + arch=KernelArch.AARCH64, gcc_cpu="cortex-a35", loader_link_address=0x80280000, kernel_options={ @@ -71,6 +93,7 @@ class ConfigInfo: ), BoardInfo( name="zcu102", + arch=KernelArch.AARCH64, gcc_cpu="cortex-a53", loader_link_address=0x40000000, kernel_options={ @@ -86,6 +109,7 @@ class ConfigInfo: ), BoardInfo( name="maaxboard", + arch=KernelArch.AARCH64, gcc_cpu="cortex-a53", loader_link_address=0x40480000, kernel_options={ @@ -100,6 +124,7 @@ class ConfigInfo: ), BoardInfo( name="imx8mm_evk", + arch=KernelArch.AARCH64, gcc_cpu="cortex-a53", loader_link_address=0x41000000, kernel_options={ @@ -114,6 +139,7 @@ class ConfigInfo: ), BoardInfo( name="imx8mq_evk", + arch=KernelArch.AARCH64, gcc_cpu="cortex-a53", loader_link_address=0x41000000, kernel_options={ @@ -128,6 +154,7 @@ class ConfigInfo: ), BoardInfo( name="odroidc2", + arch=KernelArch.AARCH64, gcc_cpu="cortex-a53", loader_link_address=0x20000000, kernel_options={ @@ -142,6 +169,7 @@ class ConfigInfo: ), BoardInfo( name="odroidc4", + arch=KernelArch.AARCH64, gcc_cpu="cortex-a55", loader_link_address=0x20000000, kernel_options={ @@ -156,6 +184,7 @@ class ConfigInfo: ), BoardInfo( name="qemu_virt_aarch64", + arch=KernelArch.AARCH64, gcc_cpu="cortex-a53", loader_link_address=0x70000000, kernel_options={ @@ -201,6 +230,15 @@ class ConfigInfo: ) +def c_toolchain(arch: KernelArch) -> str: + if arch == KernelArch.AARCH64: + return TOOLCHAIN_AARCH64 + elif arch == KernelArch.RISCV64: + return TOOLCHAIN_RISCV + else: + raise Exception("Unsupported toolchain architecture '{arch}'") + + def tar_filter(tarinfo: TarInfo) -> TarInfo: """This is used to change the tarinfo when created the .tar.gz archive. @@ -270,7 +308,7 @@ def build_sel4( build_dir: Path, board: BoardInfo, config: ConfigInfo, -) -> None: +) -> Dict[str, Any]: """Build seL4""" build_dir = build_dir / board.name / config.name / "sel4" build_dir.mkdir(exist_ok=True, parents=True) @@ -294,10 +332,11 @@ def build_sel4( config_strs.append(s) config_str = " ".join(config_strs) + toolchain = c_toolchain(board.arch) cmd = ( f"cmake -GNinja -DCMAKE_INSTALL_PREFIX={sel4_install_dir.absolute()} " f" -DPYTHON3={executable} " - f" -DCROSS_COMPILER_PREFIX={TOOLCHAIN_PREFIX}" + f" -DCROSS_COMPILER_PREFIX={toolchain}" f" {config_str} " f"-S {sel4_dir.absolute()} -B {sel4_build_dir.absolute()}") @@ -337,6 +376,11 @@ def build_sel4( copy(p, dest) dest.chmod(0o744) + gen_config_path = sel4_install_dir / "libsel4/include/kernel/gen_config.json" + with open(gen_config_path, "r") as f: + gen_config = json.load(f) + return gen_config + def build_elf_component( component_name: str, @@ -353,9 +397,15 @@ def build_elf_component( sel4_dir = root_dir / "board" / board.name / config.name build_dir = build_dir / board.name / config.name / component_name build_dir.mkdir(exist_ok=True, parents=True) + toolchain = c_toolchain(board.arch) defines_str = " ".join(f"{k}={v}" for k, v in defines) + defines_str += f" ARCH={board.arch.to_str()} BOARD={board.name} BUILD_DIR={build_dir.absolute()} SEL4_SDK={sel4_dir.absolute()} TOOLCHAIN={toolchain}" + + if board.gcc_cpu is not None: + defines_str += f" GCC_CPU={board.gcc_cpu}" + r = system( - f"BOARD={board.name} BUILD_DIR={build_dir.absolute()} GCC_CPU={board.gcc_cpu} SEL4_SDK={sel4_dir.absolute()} {defines_str} make -C {component_name}" + f"{defines_str} make -C {component_name}" ) if r != 0: raise Exception( @@ -371,7 +421,7 @@ def build_elf_component( dest.chmod(0o744) -def build_doc(root_dir): +def build_doc(root_dir: Path): output = root_dir / "doc" / "microkit_user_manual.pdf" environ["TEXINPUTS"] = "docs/style:" @@ -393,8 +443,15 @@ def build_lib_component( sel4_dir = root_dir / "board" / board.name / config.name build_dir = build_dir / board.name / config.name / component_name build_dir.mkdir(exist_ok=True, parents=True) + + toolchain = c_toolchain(board.arch) + defines_str = f" ARCH={board.arch.to_str()} BUILD_DIR={build_dir.absolute()} SEL4_SDK={sel4_dir.absolute()} TOOLCHAIN={toolchain}" + + if board.gcc_cpu is not None: + defines_str += f" GCC_CPU={board.gcc_cpu}" + r = system( - f"BUILD_DIR={build_dir.absolute()} GCC_CPU={board.gcc_cpu} SEL4_SDK={sel4_dir.absolute()} make -C {component_name}" + f"{defines_str} make -C {component_name}" ) if r != 0: raise Exception( @@ -512,13 +569,25 @@ def main() -> None: build_dir = Path("build") for board in selected_boards: for config in selected_configs: - build_sel4(sel4_dir, root_dir, build_dir, board, config) + sel4_gen_config = build_sel4(sel4_dir, root_dir, build_dir, board, config) loader_printing = 1 if config.name == "debug" else 0 loader_defines = [ ("LINK_ADDRESS", hex(board.loader_link_address)), - ("PHYSICAL_ADDRESS_BITS", 40), ("PRINTING", loader_printing) ] + # There are some architecture dependent configuration options that the loader + # needs to know about, so we figure that out here + if board.arch.is_riscv(): + loader_defines.append(("FIRST_HART_ID", sel4_gen_config["FIRST_HART_ID"])) + if board.arch.is_arm(): + if sel4_gen_config["ARM_PA_SIZE_BITS_40"]: + arm_pa_size_bits = 40 + elif sel4_gen_config["ARM_PA_SIZE_BITS_44"]: + arm_pa_size_bits = 44 + else: + raise Exception("Unexpected ARM physical address bits defines") + loader_defines.append(("PHYSICAL_ADDRESS_BITS", arm_pa_size_bits)) + build_elf_component("loader", root_dir, build_dir, board, config, loader_defines) build_elf_component("monitor", root_dir, build_dir, board, config, []) build_lib_component("libmicrokit", root_dir, build_dir, board, config) diff --git a/docs/manual.md b/docs/manual.md index 2394e6da9..48511af56 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -648,8 +648,8 @@ The `map` element has the following attributes: * `mr`: Identifies the memory region to map. * `vaddr`: Identifies the virtual address at which to map the memory region. * `perms`: Identifies the permissions with which to map the memory region. Can be a combination of `r` (read), `w` (write), and `x` (eXecute), with the exception of a write-only mapping (just `w`). -* `cached`: Determines if mapped with caching enabled or disabled. Defaults to `true`. -* `setvar_vaddr`: Specifies a symbol in the program image. This symbol will be rewritten with the virtual address of the memory region. +* `cached`: (optional) Determines if mapped with caching enabled or disabled. Defaults to `true`. +* `setvar_vaddr`: (optional) Specifies a symbol in the program image. This symbol will be rewritten with the virtual address of the memory region. The `irq` element has the following attributes: @@ -705,6 +705,11 @@ Below are the available page sizes for each architecture that Microkit supports. * 0x1000 (4KiB) * 0x200000 (2MiB) +#### RISC-V 64-bit + +* 0x1000 (4KiB) +* 0x200000 (2MiB) + ## `channel` The `channel` element has exactly two `end` children elements for specifying the two PDs associated with the channel. @@ -879,8 +884,7 @@ To avoid this behaviour, the call to `armv8_switch_to_el1` should be replaced wi ## Adding Platform Support -The following section is a guide for adding support for a new platform to Microkit. Currently only AArch64 -is supported in Microkit, so this guide assumes you are trying to add support for an AArch64 platform. +The following section is a guide for adding support for a new platform to Microkit. ### Prerequisites @@ -903,6 +907,8 @@ The other component of Microkit that is platform dependent is the loader itself. the UART for debug output which requires a basic `putc` implementation. The UART device used in the loader should be the same as what is used for the seL4 kernel debug output. +It should be noted that on RISC-V platforms, the SBI will be used for `putc` so no porting is necessary. + Once you have patched the loader and the SDK build script, there should be no other changes required to have a working platform port. It is a good idea at this point to boot a hello world system to confirm the port is working. diff --git a/libmicrokit/Makefile b/libmicrokit/Makefile index 3dd5f0b76..5df25f37c 100644 --- a/libmicrokit/Makefile +++ b/libmicrokit/Makefile @@ -7,21 +7,42 @@ ifeq ($(strip $(BUILD_DIR)),) $(error BUILD_DIR must be specified) endif -ifeq ($(strip $(GCC_CPU)),) -$(error GCC_CPU must be specified) +ifeq ($(strip $(ARCH)),) +$(error ARCH must be specified) endif -TOOLCHAIN := aarch64-none-elf- -CFLAGS := -std=gnu11 -g -O3 -nostdlib -ffreestanding -mcpu=$(GCC_CPU) -Wall -Wno-maybe-uninitialized -Wno-unused-function -Werror -Iinclude -I$(SEL4_SDK)/include +ifeq ($(strip $(TOOLCHAIN)),) +$(error TOOLCHAIN must be specified) +endif + +ifeq ($(ARCH),aarch64) + ASM_FLAGS := -mcpu=$(GCC_CPU) + CFLAGS_AARCH64 := -mcpu=$(GCC_CPU) + CFLAGS_ARCH := $(CFLAGS_AARCH64) + ARCH_DIR := aarch64 +else ifeq ($(ARCH),riscv64) + ASM_FLAGS := -march=rv64imafdc_zicsr_zifencei -mabi=lp64d + CFLAGS_RISCV64 := -mcmodel=medany -march=rv64imafdc_zicsr_zifencei -mabi=lp64d + CFLAGS_ARCH := $(CFLAGS_RISCV64) + ARCH_DIR := riscv +endif + +CFLAGS := -std=gnu11 \ + -g -O3 -nostdlib \ + -ffreestanding \ + -Wall -Wno-maybe-uninitialized \ + -Wno-unused-function -Werror \ + -Iinclude -I$(SEL4_SDK)/include \ + $(CFLAGS_ARCH) LIBS := libmicrokit.a OBJS := main.o crt0.o dbg.o -$(BUILD_DIR)/%.o : src/%.S - $(TOOLCHAIN)gcc -x assembler-with-cpp -c -g -mcpu=$(GCC_CPU) $< -o $@ +$(BUILD_DIR)/%.o : src/$(ARCH_DIR)/%.S + $(TOOLCHAIN)gcc -x assembler-with-cpp -c $(CFLAGS) $< -o $@ -$(BUILD_DIR)/%.o : src/%.s - $(TOOLCHAIN)as -g -mcpu=$(GCC_CPU) $< -o $@ +$(BUILD_DIR)/%.o : src/$(ARCH_DIR)/%.s + $(TOOLCHAIN)as -g $(ASM_FLAGS) $< -o $@ $(BUILD_DIR)/%.o : src/%.c $(TOOLCHAIN)gcc -c $(CFLAGS) $< -o $@ diff --git a/libmicrokit/microkit.ld b/libmicrokit/microkit.ld index 6cc345af8..c2a78b841 100644 --- a/libmicrokit/microkit.ld +++ b/libmicrokit/microkit.ld @@ -40,13 +40,19 @@ SECTIONS { _data = .; *(.data) + . = ALIGN(8); + __global_pointer$ = . + 0x800; + *(.srodata) + *(.sdata) _data_end = .; } :data .bss : { _bss = .; + *(.sbss) *(.bss) + *(.bss*) *(COMMON) . = ALIGN(4); _bss_end = .; diff --git a/libmicrokit/src/crt0.s b/libmicrokit/src/aarch64/crt0.s similarity index 100% rename from libmicrokit/src/crt0.s rename to libmicrokit/src/aarch64/crt0.s diff --git a/libmicrokit/src/riscv/crt0.s b/libmicrokit/src/riscv/crt0.s new file mode 100644 index 000000000..28d38a98c --- /dev/null +++ b/libmicrokit/src/riscv/crt0.s @@ -0,0 +1,20 @@ +/* + * Copyright 2024, UNSW + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +.extern main +.extern __global_pointer$ + +.section ".text.start" + +.global _start; +.type _start, %function; +_start: +.option push +.option norelax +1: auipc gp, %pcrel_hi(__global_pointer$) + addi gp, gp, %pcrel_lo(1b) +.option pop + j main diff --git a/loader/Makefile b/loader/Makefile index cbae7e41e..4195f1e34 100644 --- a/loader/Makefile +++ b/loader/Makefile @@ -7,8 +7,8 @@ ifeq ($(strip $(BUILD_DIR)),) $(error BUILD_DIR must be specified) endif -ifeq ($(strip $(GCC_CPU)),) -$(error GCC_CPU must be specified) +ifeq ($(strip $(ARCH)),) +$(error ARCH must be specified) endif ifeq ($(strip $(BOARD)),) @@ -19,30 +19,48 @@ ifeq ($(strip $(LINK_ADDRESS)),) $(error LINK_ADDRESS must be specified) endif -ifeq ($(strip $(PHYSICAL_ADDRESS_BITS)),) -$(error PHYSICAL_ADDRESS_BITS must be specified) +ifeq ($(strip $(TOOLCHAIN)),) +$(error TOOLCHAIN must be specified) endif ifeq ($(strip $(PRINTING)),) $(error PRINTING must be specified) endif -TOOLCHAIN := aarch64-none-elf- -CFLAGS := -std=gnu11 -g -O3 -nostdlib -ffreestanding -mcpu=$(GCC_CPU) -DPRINTING=$(PRINTING) -DBOARD_$(BOARD) -Wno-unused-function -Wall -Werror -mgeneral-regs-only +ifeq ($(ARCH),aarch64) + CFLAGS_AARCH64 := -DPHYSICAL_ADDRESS_BITS=$(PHYSICAL_ADDRESS_BITS) -mcpu=$(GCC_CPU) -mgeneral-regs-only + CFLAGS_ARCH := $(CFLAGS_AARCH64) -DARCH_aarch64 + ASM_FLAGS_ARCH := -DPHYSICAL_ADDRESS_BITS=$(PHYSICAL_ADDRESS_BITS) -mcpu=$(GCC_CPU) + ARCH_DIR := aarch64 +else ifeq ($(ARCH),riscv64) + CFLAGS_RISCV64 := -mcmodel=medany -march=rv64imac_zicsr_zifencei -mabi=lp64 + CFLAGS_ARCH := $(CFLAGS_RISCV64) -DARCH_riscv64 + ASM_FLAGS_ARCH := -march=rv64imac_zicsr_zifencei -mabi=lp64 -DFIRST_HART_ID=$(FIRST_HART_ID) + ARCH_DIR := riscv +endif + +CFLAGS := -std=gnu11 -g -O3 -nostdlib -ffreestanding $(CFLAGS_ARCH) -DBOARD_$(BOARD) -DPRINTING=$(PRINTING) -Wall -Werror -Wno-unused-function + +ASM_FLAGS := $(ASM_FLAGS_ARCH) -g PROGS := loader.elf -OBJECTS := loader.o crt0.o util64.o -LINKSCRIPT_INPUT := loader.ld +OBJECTS := loader.o crt0.o + +ifeq ($(ARCH),aarch64) + OBJECTS += util64.o +endif + +LINKSCRIPT_INPUT := $(ARCH).ld LINKSCRIPT := $(BUILD_DIR)/link.ld -$(BUILD_DIR)/%.o : src/%.S - $(TOOLCHAIN)gcc -x assembler-with-cpp -c -g -DPHYSICAL_ADDRESS_BITS=$(PHYSICAL_ADDRESS_BITS) -mcpu=$(GCC_CPU) $< -o $@ +$(BUILD_DIR)/%.o : src/$(ARCH_DIR)/%.S + $(TOOLCHAIN)gcc -x assembler-with-cpp -c $(ASM_FLAGS) $< -o $@ -$(BUILD_DIR)/%.o : src/%.s - $(TOOLCHAIN)as -g -mcpu=$(GCC_CPU) $< -o $@ +$(BUILD_DIR)/%.o : src/$(ARCH_DIR)/%.s + $(TOOLCHAIN)as $< -o $@ $(BUILD_DIR)/%.o : src/%.c - $(TOOLCHAIN)gcc -c $(CFLAGS) $< -o $@ + $(TOOLCHAIN)gcc -c $(CFLAGS) $< -o $@ OBJPROG = $(addprefix $(BUILD_DIR)/, $(PROGS)) diff --git a/loader/loader.ld b/loader/aarch64.ld similarity index 100% rename from loader/loader.ld rename to loader/aarch64.ld diff --git a/loader/riscv64.ld b/loader/riscv64.ld new file mode 100644 index 000000000..f7ae1240e --- /dev/null +++ b/loader/riscv64.ld @@ -0,0 +1,43 @@ +/* + * Copyright 2021, Breakaway Consulting Pty. Ltd. + * + * SPDX-License-Identifier: BSD-2-Clause + */ +PHDRS +{ + all PT_LOAD AT (LINK_ADDRESS); +} + +SECTIONS +{ + . = LINK_ADDRESS; + + .text : + { + _text = .; + *(.text.start) + *(.text*) + *(.rodata) + _text_end = .; + } :all + + .data : + { + _data = .; + *(.data) + __global_pointer$ = . + 0x800; + *(.srodata) + *(.sdata) + _data_end = .; + } :all + + .bss : + { + _bss = .; + *(.sbss) + *(.bss) + *(COMMON) + . = ALIGN(4); + _bss_end = .; + } :all +} diff --git a/loader/src/crt0.s b/loader/src/aarch64/crt0.s similarity index 100% rename from loader/src/crt0.s rename to loader/src/aarch64/crt0.s diff --git a/loader/src/util64.S b/loader/src/aarch64/util64.S similarity index 100% rename from loader/src/util64.S rename to loader/src/aarch64/util64.S diff --git a/loader/src/loader.c b/loader/src/loader.c index e5a949759..8706d2ea8 100644 --- a/loader/src/loader.c +++ b/loader/src/loader.c @@ -4,7 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ #include -#include +#include _Static_assert(sizeof(uintptr_t) == 8 || sizeof(uintptr_t) == 4, "Expect uintptr_t to be 32-bit or 64-bit"); @@ -88,6 +88,7 @@ void el2_mmu_enable(void); char _stack[STACK_SIZE] ALIGN(16); +#ifdef ARCH_aarch64 /* Paging structures for kernel mapping */ uint64_t boot_lvl0_upper[1 << 9] ALIGN(1 << 12); uint64_t boot_lvl1_upper[1 << 9] ALIGN(1 << 12); @@ -98,7 +99,15 @@ uint64_t boot_lvl0_lower[1 << 9] ALIGN(1 << 12); uint64_t boot_lvl1_lower[1 << 9] ALIGN(1 << 12); uintptr_t exception_register_state[32]; +#elif defined(ARCH_riscv64) +/* Paging structures for kernel mapping */ +uint64_t boot_lvl1_pt[1 << 9] ALIGN(1 << 12); +uint64_t boot_lvl2_pt[1 << 9] ALIGN(1 << 12); +/* Paging structures for identity mapping */ +uint64_t boot_lvl2_pt_elf[1 << 9] ALIGN(1 << 12); +#endif +extern char _text; extern char _bss_end; const struct loader_data *loader_data = (void *) &_bss_end; @@ -207,6 +216,29 @@ static void putc(uint8_t ch) while ((*UART_REG(UARTFR) & PL011_UARTFR_TXFF) != 0); *UART_REG(UARTDR) = ch; } + +#elif defined(ARCH_riscv64) +#define SBI_CONSOLE_PUTCHAR 1 + +// TODO: remove, just do straight ASM +#define SBI_CALL(which, arg0, arg1, arg2) ({ \ + register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0); \ + register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1); \ + register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2); \ + register uintptr_t a7 asm ("a7") = (uintptr_t)(which); \ + asm volatile ("ecall" \ + : "+r" (a0) \ + : "r" (a1), "r" (a2), "r" (a7) \ + : "memory"); \ + a0; \ +}) + +#define SBI_CALL_1(which, arg0) SBI_CALL(which, arg0, 0, 0) + +static void putc(uint8_t ch) +{ + SBI_CALL_1(SBI_CONSOLE_PUTCHAR, ch); +} #else #error Board not defined #endif @@ -252,6 +284,7 @@ static void puthex64(uint64_t val) puts(buffer); } +#ifdef ARCH_aarch64 static void puthex(uintptr_t val) { #if WORD_SIZE == 32 @@ -394,6 +427,7 @@ static char *ec_to_string(uintptr_t ec) } return ""; } +#endif /* * Print out the loader data structure. @@ -459,6 +493,7 @@ static void copy_data(void) } } +#ifdef ARCH_aarch64 static int ensure_correct_el(void) { enum el el = current_el(); @@ -509,6 +544,7 @@ static int ensure_correct_el(void) return 0; } +#endif static void start_kernel(void) { @@ -569,12 +605,35 @@ static void configure_gicv2(void) } #endif +#ifdef ARCH_riscv64 -int main(void) +/* + * This is the encoding for the MODE field of the satp register when + * implementing 39-bit virtual address spaces (known as Sv39). + */ +#define VM_MODE (0x8llu << 60) + +#define RISCV_PGSHIFT 12 + +static inline void enable_mmu(void) { - int r; - enum el el; + // The RISC-V privileged spec (20211203), section 4.1.11 says that the + // SFENCE.VMA instruction may need to be executed before or after writing + // to satp. I don't understand why we do it before compared to after. + // Need to understand 4.2.1 of the spec. + asm volatile("sfence.vma" ::: "memory"); + asm volatile( + "csrw satp, %0\n" + : + : "r"(VM_MODE | (uintptr_t)boot_lvl1_pt >> RISCV_PGSHIFT) + : + ); + asm volatile("fence.i" ::: "memory"); +} +#endif +int main(void) +{ #if defined(BOARD_zcu102) uart_init(); #endif @@ -583,7 +642,7 @@ int main(void) /* Check that the loader magic number is set correctly */ if (loader_data->magic != MAGIC) { puts("LDR|ERROR: mismatch on loader data structure magic number\n"); - return 1; + goto fail; } print_loader_data(); @@ -597,6 +656,9 @@ int main(void) configure_gicv2(); #endif +#ifdef ARCH_aarch64 + int r; + enum el el; r = ensure_correct_el(); if (r != 0) { goto fail; @@ -611,6 +673,10 @@ int main(void) } else { puts("LDR|ERROR: unknown EL level for MMU enable\n"); } +#elif defined(ARCH_riscv64) + puts("LDR|INFO: enabling MMU\n"); + enable_mmu(); +#endif puts("LDR|INFO: jumping to kernel\n"); start_kernel(); @@ -626,6 +692,7 @@ int main(void) } } +#ifdef ARCH_aarch64 void exception_handler(uintptr_t ex, uintptr_t esr, uintptr_t far) { uintptr_t ec = (esr >> 26) & 0x3f; @@ -654,3 +721,4 @@ void exception_handler(uintptr_t ex, uintptr_t esr, uintptr_t far) for (;;) { } } +#endif diff --git a/loader/src/riscv/crt0.S b/loader/src/riscv/crt0.S new file mode 100644 index 000000000..fd27c9c1e --- /dev/null +++ b/loader/src/riscv/crt0.S @@ -0,0 +1,132 @@ +/* + * Copyright 2024, UNSW + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +.extern main +.extern __global_pointer$ + +#ifndef FIRST_HART_ID +#error "Loader must be defined with FIRST_HART_ID" +#endif + +.section ".text.start" + +#define STACK_SIZE 4096 + +/* SBI commands for the HSM extension */ +#define SBI_HSM_BASE_EID 0x48534DULL +#define SBI_HSM_BASE_HART_START_FID 0 +#define SBI_HSM_BASE_HART_STOP_FID 1 +/* SBI commands for the base extension */ +#define SBI_EXT_BASE_EID 0x10 +#define SBI_EXT_BASE_PROBE_EXT_FID 3 + +/* Unfortunately, the latest version of the SBI (v1.0.0) specification does not + * specify the starting state of supervisor software. + * OpenSBI gives the following: + * a0: current hart ID + * a1: address of the DTB (given by previous booting stage) + * However, these parameters are not enforced by the specification. Therefore, if you were + * to use a different SBI implementation or your own, it might not be compatible with + * this loader. + * The hart ID is required as seL4 expects to be booted on CONFIG_FIRST_HART_ID. + * The address of the DTB is passed to seL4 which is then passed to the initial + * task (via the BootInfo frame). + * + * On RISC-V, only M-Mode can access the CSR mhartid to get the actual hart ID, + * the SBI running there is responsible for passing this ID up. In S-Mode there + * is no way to ever query it again, so we have to preserve what we get passed + * here. This is a RISC-V design decision, more background can be found at + * https://github.com/riscv/riscv-sbi-doc/issues/25. + * + * It seems that OpenSBI starts us at a random hart and keeps all other harts + * suspended or spinning. However, even on non-SMP configurations there might + * be an expectation that we are running on FIRST_HART_ID + * hart turns out to be a different one, we have to switch harts somehow. The + * SBI Heart State Management (HSM) extension exists for this, but it might not + * be implemented. In this case, there is nothing we can do here in the assembly + * startup code, but C boot code might still have platform specific proprietary + * ways to switch harts. + */ + +.global _start +_start: + +.option push +.option norelax +1:auipc gp, %pcrel_hi(__global_pointer$) + addi gp, gp, %pcrel_lo(1b) +.option pop + + /* save the parameters passed */ + mv s0, a0 /* preserve a0 (hart id) in s0 */ + mv s2, a1 /* preserve a1 (dtb) in s2 */ + + /* Attach the stack to sp before calling any C functions */ + la sp, (_stack + STACK_SIZE) + + /* Check if the Heart State Management (HSM) extension exists, so it can be + * used to switch harts if we are not running on hart FIRST_HART_ID + * The SBI returns SBI_SUCCESS (0) in a0 if the call could be processed or an + * error code if not. On SBI_SUCCESS the value in a1 is 0 if the extension is + * not available or an extension-specific non-zero value if it is available. + */ + li a7, SBI_EXT_BASE_EID + li a6, SBI_EXT_BASE_PROBE_EXT_FID + li a0, SBI_HSM_BASE_EID + ecall /* call SBI to probe for HSM extension */ + mv a2, a0 /* move SBI call generic return code to a2 as we need a0 */ + mv a3, a1 /* move SBI call error return code to a3 as we need a1 */ + mv a0, s0 /* restore a0 to hold hart ID passed by the boot loader */ + mv a1, s2 /* restore a1 to hold dtb address passed by the boot loader */ + bnez a2, _start1 /* goto _start1 if SBI did not return SBI_SUCCESS (0) */ + beqz a3, _start1 /* goto _start1 if HSM extension is missing */ + + /* Check if we are running on the hart we expect on, FIRST_HART_ID */ + li s1, FIRST_HART_ID + beq a0, s1, _start1 /* goto _start1 if we are on FIRST_HART_ID */ + + /* Use HSM extension to start hart FIRST_HART_ID. */ +hsm_switch_hart: + li a7, SBI_HSM_BASE_EID + li a6, SBI_HSM_BASE_HART_START_FID + li a0, FIRST_HART_ID + mv a2, s2 /* dtb address to be passed in a1 when new hart starts is 3rd parameter */ + la a1, _start1 /* where to start the hart */ + ecall /* call SBI to start hart FIRST_HART_ID */ + + /* Since we are not the designated primary hart, continue the boot process as + * secondary hart + */ + mv a0, s0 /* restore a0 to hold hart ID passed by OpenSBI */ + j spin_hart /* Spin any hart that isn't FIRST_HART_ID since we only support single-core right now. */ + + +_start1: /* a0 must hold current hard ID passed by bootloader */ + /* a1 must hold dtb address passed by bootloader */ +.option push +.option norelax +1:auipc gp, %pcrel_hi(__global_pointer$) + addi gp, gp, %pcrel_lo(1b) +.option pop + + la sp, (_stack + STACK_SIZE) + la s0, main + jr s0 + +.text + +.global secondary_harts +secondary_harts: + +.option push +.option norelax +1:auipc gp, %pcrel_hi(__global_pointer$) + addi gp, gp, %pcrel_lo(1b) +.option pop + +spin_hart: + wfi + j spin_hart diff --git a/monitor/Makefile b/monitor/Makefile index 194c96010..3479d5b1a 100644 --- a/monitor/Makefile +++ b/monitor/Makefile @@ -7,22 +7,41 @@ ifeq ($(strip $(BUILD_DIR)),) $(error BUILD_DIR must be specified) endif -ifeq ($(strip $(GCC_CPU)),) -$(error GCC_CPU must be specified) +ifeq ($(strip $(ARCH)),) +$(error ARCH must be specified) endif -TOOLCHAIN := aarch64-none-elf- -CFLAGS := -std=gnu11 -g -O3 -nostdlib -ffreestanding -mcpu=$(GCC_CPU) -Wall -Wno-maybe-uninitialized -Werror -I$(SEL4_SDK)/include +ifeq ($(strip $(TOOLCHAIN)),) +$(error TOOLCHAIN must be specified) +endif + +ifeq ($(ARCH),aarch64) + CFLAGS_ARCH := -mcpu=$(GCC_CPU) + ASM_CPP_FLAGS := -x assembler-with-cpp -c -g -mcpu=$(GCC_CPU) + ASM_FLAGS := -mcpu=$(GCC_CPU) + + ARCH_DIR := aarch64 +else ifeq ($(ARCH),riscv64) + ASM_CPP_FLAGS := -x assembler-with-cpp -c -g -march=rv64imac_zicsr_zifencei -mabi=lp64 + ASM_FLAGS := -march=rv64imac_zicsr_zifencei -mabi=lp64 + CFLAGS_ARCH := -mcmodel=medany -march=rv64imac_zicsr_zifencei -mabi=lp64 -DARCH_riscv64 + + ARCH_DIR := riscv +else + $(error ARCH is unsupported) +endif + +CFLAGS := -std=gnu11 -g -O3 -nostdlib -ffreestanding -Wall -Wno-maybe-uninitialized -Werror -I$(SEL4_SDK)/include $(CFLAGS_ARCH) -DARCH_$(ARCH) PROGS := monitor.elf OBJECTS := main.o crt0.o debug.o util.o LINKSCRIPT := monitor.ld -$(BUILD_DIR)/%.o : src/%.S - $(TOOLCHAIN)gcc -x assembler-with-cpp -c -g -mcpu=$(GCC_CPU) $< -o $@ +$(BUILD_DIR)/%.o : src/$(ARCH_DIR)/%.S + $(TOOLCHAIN)gcc $(ASM_CPP_FLAGS) $< -o $@ -$(BUILD_DIR)/%.o : src/%.s - $(TOOLCHAIN)as -g -mcpu=$(GCC_CPU) $< -o $@ +$(BUILD_DIR)/%.o : src/$(ARCH_DIR)/%.s + $(TOOLCHAIN)as -g $(ASM_FLAGS) $< -o $@ $(BUILD_DIR)/%.o : src/%.c $(TOOLCHAIN)gcc -c $(CFLAGS) $< -o $@ diff --git a/monitor/monitor.ld b/monitor/monitor.ld index c9985d689..706f4c8b3 100644 --- a/monitor/monitor.ld +++ b/monitor/monitor.ld @@ -25,12 +25,16 @@ SECTIONS { _data = .; *(.data) + __global_pointer$ = . + 0x800; + *(.srodata) + *(.sdata) _data_end = .; } :all .bss : { _bss = .; + *(.sbss) *(.bss) *(COMMON) . = ALIGN(4); diff --git a/monitor/src/crt0.s b/monitor/src/aarch64/crt0.s similarity index 100% rename from monitor/src/crt0.s rename to monitor/src/aarch64/crt0.s diff --git a/monitor/src/main.c b/monitor/src/main.c index e300ddd63..0b644c639 100644 --- a/monitor/src/main.c +++ b/monitor/src/main.c @@ -113,6 +113,82 @@ seL4_Word *system_invocation_data = (void *)0x80000000; struct untyped_info untyped_info; +void dump_untyped_info() +{ + puts("\nUntyped Info Memory Ranges\n"); + seL4_Word start = untyped_info.regions[0].paddr; + seL4_Word end = start + (1ULL << untyped_info.regions[0].size_bits); + seL4_Word is_device = untyped_info.regions[0].is_device; + for (int i = 1; i < untyped_info.cap_end - untyped_info.cap_start; i++) { + if (untyped_info.regions[i].paddr != end || untyped_info.regions[i].is_device != is_device) { + puts(" paddr: "); + puthex64(start); + puts(" - "); + puthex64(end); + puts(" ("); + puts(is_device ? "device" : "normal"); + puts(")\n"); + start = untyped_info.regions[i].paddr; + end = start + (1ULL << untyped_info.regions[i].size_bits); + is_device = untyped_info.regions[i].is_device; + } else { + end += (1ULL << untyped_info.regions[i].size_bits); + } + } + puts(" paddr: "); + puthex64(start); + puts(" - "); + puthex64(end); + puts(" ("); + puts(is_device ? "device" : "normal"); + puts(")\n"); +} + +/* + * Convert the fault status register given by the kernel into a string describing + * what fault happened. The FSR is the 'scause' register. + */ +#ifdef ARCH_riscv64 +static char *riscv_fsr_to_string(seL4_Word fsr) +{ + switch (fsr) { + case 0: + return "Instruction address misaligned"; + case 1: + return "Instruction access fault"; + case 2: + return "Illegal instruction"; + case 3: + return "Breakpoint"; + case 4: + return "Load address misaligned"; + case 5: + return "Load access fault"; + case 6: + return "Store/AMO address misaligned"; + case 7: + return "Store/AMO access fault"; + case 8: + return "Environment call from U-mode"; + case 9: + return "Environment call from S-mode"; + case 12: + return "Instruction page fault"; + case 13: + return "Load page fault"; + case 15: + return "Store/AMO page fault"; + case 18: + return "Software check"; + case 19: + return "Hardware error"; + default: + return ""; + } +} +#endif + +#ifdef ARCH_aarch64 static char *ec_to_string(uintptr_t ec) { switch (ec) { @@ -244,6 +320,7 @@ static char *data_abort_dfsc_to_string(uintptr_t dfsc) } return ""; } +#endif static void check_untypeds_match(seL4_BootInfo *bi) { @@ -429,6 +506,215 @@ static unsigned perform_invocation(seL4_Word *invocation_data, unsigned offset, return next_offset; } +static void print_tcb_registers(seL4_UserContext *regs) +{ +#if defined(ARCH_riscv64) + puts("Registers: \n"); + puts("pc : "); + puthex64(regs->pc); + puts("\n"); + puts("ra : "); + puthex64(regs->ra); + puts("\n"); + puts("s0 : "); + puthex64(regs->s0); + puts("\n"); + puts("s1 : "); + puthex64(regs->s1); + puts("\n"); + puts("s2 : "); + puthex64(regs->s2); + puts("\n"); + puts("s3 : "); + puthex64(regs->s3); + puts("\n"); + puts("s4 : "); + puthex64(regs->s4); + puts("\n"); + puts("s5 : "); + puthex64(regs->s5); + puts("\n"); + puts("s6 : "); + puthex64(regs->s6); + puts("\n"); + puts("s7 : "); + puthex64(regs->s7); + puts("\n"); + puts("s8 : "); + puthex64(regs->s8); + puts("\n"); + puts("s9 : "); + puthex64(regs->s9); + puts("\n"); + puts("s10 : "); + puthex64(regs->s10); + puts("\n"); + puts("s11 : "); + puthex64(regs->s11); + puts("\n"); + puts("a0 : "); + puthex64(regs->a0); + puts("\n"); + puts("a1 : "); + puthex64(regs->a1); + puts("\n"); + puts("a2 : "); + puthex64(regs->a2); + puts("\n"); + puts("a3 : "); + puthex64(regs->a3); + puts("\n"); + puts("a4 : "); + puthex64(regs->a4); + puts("\n"); + puts("a5 : "); + puthex64(regs->a5); + puts("\n"); + puts("a6 : "); + puthex64(regs->a6); + puts("\n"); + puts("t0 : "); + puthex64(regs->t0); + puts("\n"); + puts("t1 : "); + puthex64(regs->t1); + puts("\n"); + puts("t2 : "); + puthex64(regs->t2); + puts("\n"); + puts("t3 : "); + puthex64(regs->t3); + puts("\n"); + puts("t4 : "); + puthex64(regs->t4); + puts("\n"); + puts("t5 : "); + puthex64(regs->t5); + puts("\n"); + puts("t6 : "); + puthex64(regs->t6); + puts("\n"); + puts("tp : "); + puthex64(regs->tp); + puts("\n"); +#elif defined(ARCH_aarch64) + // FIXME: Would be good to print the whole register set + puts("Registers: \n"); + puts("pc : "); + puthex64(regs->pc); + puts("\n"); + puts("spsr : "); + puthex64(regs->spsr); + puts("\n"); + puts("x0 : "); + puthex64(regs->x0); + puts("\n"); + puts("x1 : "); + puthex64(regs->x1); + puts("\n"); + puts("x2 : "); + puthex64(regs->x2); + puts("\n"); + puts("x3 : "); + puthex64(regs->x3); + puts("\n"); + puts("x4 : "); + puthex64(regs->x4); + puts("\n"); + puts("x5 : "); + puthex64(regs->x5); + puts("\n"); + puts("x6 : "); + puthex64(regs->x6); + puts("\n"); + puts("x7 : "); + puthex64(regs->x7); + puts("\n"); +#endif +} + +#ifdef ARCH_riscv64 +static void riscv_print_vm_fault() +{ + seL4_Word ip = seL4_GetMR(seL4_VMFault_IP); + seL4_Word fault_addr = seL4_GetMR(seL4_VMFault_Addr); + seL4_Word is_instruction = seL4_GetMR(seL4_VMFault_PrefetchFault); + seL4_Word fsr = seL4_GetMR(seL4_VMFault_FSR); + puts("MON|ERROR: VMFault: ip="); + puthex64(ip); + puts(" fault_addr="); + puthex64(fault_addr); + puts(" fsr="); + puthex64(fsr); + puts(" "); + puts(is_instruction ? "(instruction fault)" : "(data fault)"); + puts("\n"); + puts("MON|ERROR: description of fault: "); + puts(riscv_fsr_to_string(fsr)); + puts("\n"); +} +#endif + +#ifdef ARCH_aarch64 +static void aarch64_print_vm_fault() +{ + seL4_Word ip = seL4_GetMR(seL4_VMFault_IP); + seL4_Word fault_addr = seL4_GetMR(seL4_VMFault_Addr); + seL4_Word is_instruction = seL4_GetMR(seL4_VMFault_PrefetchFault); + seL4_Word fsr = seL4_GetMR(seL4_VMFault_FSR); + seL4_Word ec = fsr >> 26; + seL4_Word il = fsr >> 25 & 1; + seL4_Word iss = fsr & 0x1ffffffUL; + puts("MON|ERROR: VMFault: ip="); + puthex64(ip); + puts(" fault_addr="); + puthex64(fault_addr); + puts(" fsr="); + puthex64(fsr); + puts(" "); + puts(is_instruction ? "(instruction fault)" : "(data fault)"); + puts("\n"); + puts("MON|ERROR: ec: "); + puthex32(ec); + puts(" "); + puts(ec_to_string(ec)); + puts(" il: "); + puts(il ? "1" : "0"); + puts(" iss: "); + puthex32(iss); + puts("\n"); + + if (ec == 0x24) { + /* FIXME: Note, this is not a complete decoding of the fault! Just some of the more + common fields! + */ + seL4_Word dfsc = iss & 0x3f; + bool ea = (iss >> 9) & 1; + bool cm = (iss >> 8) & 1; + bool s1ptw = (iss >> 7) & 1; + bool wnr = (iss >> 6) & 1; + puts("MON|ERROR: dfsc = "); + puts(data_abort_dfsc_to_string(dfsc)); + puts(" ("); + puthex32(dfsc); + puts(")"); + if (ea) { + puts(" -- external abort"); + } + if (cm) { + puts(" -- cache maint"); + } + if (s1ptw) { + puts(" -- stage 2 fault for stage 1 page table walk"); + } + if (wnr) { + puts(" -- write not read"); + } + puts("\n"); + } +} +#endif + static void monitor(void) { for (;;) { @@ -478,38 +764,7 @@ static void monitor(void) fail("error reading registers"); } - // FIXME: Would be good to print the whole register set - puts("MON|ERROR: Registers: \n"); - puts("MON|ERROR: pc : "); - puthex64(regs.pc); - puts("\n"); - puts("MON|ERROR: spsr : "); - puthex64(regs.spsr); - puts("\n"); - puts("MON|ERROR: x0 : "); - puthex64(regs.x0); - puts("\n"); - puts("MON|ERROR: x1 : "); - puthex64(regs.x1); - puts("\n"); - puts("MON|ERROR: x2 : "); - puthex64(regs.x2); - puts("\n"); - puts("MON|ERROR: x3 : "); - puthex64(regs.x3); - puts("\n"); - puts("MON|ERROR: x4 : "); - puthex64(regs.x4); - puts("\n"); - puts("MON|ERROR: x5 : "); - puthex64(regs.x5); - puts("\n"); - puts("MON|ERROR: x6 : "); - puthex64(regs.x6); - puts("\n"); - puts("MON|ERROR: x7 : "); - puthex64(regs.x7); - puts("\n"); + print_tcb_registers(®s); switch (label) { case seL4_Fault_CapFault: { @@ -575,60 +830,13 @@ static void monitor(void) break; } case seL4_Fault_VMFault: { - seL4_Word ip = seL4_GetMR(seL4_VMFault_IP); - seL4_Word fault_addr = seL4_GetMR(seL4_VMFault_Addr); - seL4_Word is_instruction = seL4_GetMR(seL4_VMFault_PrefetchFault); - seL4_Word fsr = seL4_GetMR(seL4_VMFault_FSR); - seL4_Word ec = fsr >> 26; - seL4_Word il = fsr >> 25 & 1; - seL4_Word iss = fsr & 0x1ffffffUL; - puts("MON|ERROR: VMFault: ip="); - puthex64(ip); - puts(" fault_addr="); - puthex64(fault_addr); - puts(" fsr="); - puthex64(fsr); - puts(" "); - puts(is_instruction ? "(instruction fault)" : "(data fault)"); - puts("\n"); - puts("MON|ERROR: ec: "); - puthex32(ec); - puts(" "); - puts(ec_to_string(ec)); - puts(" il: "); - puts(il ? "1" : "0"); - puts(" iss: "); - puthex32(iss); - puts("\n"); - - if (ec == 0x24) { - /* FIXME: Note, this is not a complete decoding of the fault! Just some of the more - common fields! - */ - seL4_Word dfsc = iss & 0x3f; - bool ea = (iss >> 9) & 1; - bool cm = (iss >> 8) & 1; - bool s1ptw = (iss >> 7) & 1; - bool wnr = (iss >> 6) & 1; - puts("MON|ERROR: dfsc = "); - puts(data_abort_dfsc_to_string(dfsc)); - puts(" ("); - puthex32(dfsc); - puts(")"); - if (ea) { - puts(" -- external abort"); - } - if (cm) { - puts(" -- cache maint"); - } - if (s1ptw) { - puts(" -- stage 2 fault for stage 1 page table walk"); - } - if (wnr) { - puts(" -- write not read"); - } - puts("\n"); - } +#if defined(ARCH_aarch64) + aarch64_print_vm_fault(); +#elif defined(ARCH_riscv64) + riscv_print_vm_fault(); +#else +#error "Unknown architecture to print a VM fault for" +#endif break; } @@ -649,6 +857,7 @@ void main(seL4_BootInfo *bi) * if there are problems */ dump_bootinfo(bi); + dump_untyped_info(); #endif check_untypeds_match(bi); diff --git a/monitor/src/riscv/crt0.s b/monitor/src/riscv/crt0.s new file mode 100644 index 000000000..0cb0b4fea --- /dev/null +++ b/monitor/src/riscv/crt0.s @@ -0,0 +1,22 @@ +/* + * Copyright 2024, UNSW + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +.extern main +.extern __global_pointer$ + +.section ".text.start" + +.global _start; +.type _start, %function; +_start: +.option push +.option norelax +1: auipc gp, %pcrel_hi(__global_pointer$) + addi gp, gp, %pcrel_lo(1b) +.option pop + la s1, (_stack + 0xff0) + mv sp, s1 + j main diff --git a/tool/microkit/src/elf.rs b/tool/microkit/src/elf.rs index fa66e705c..6eb27b101 100644 --- a/tool/microkit/src/elf.rs +++ b/tool/microkit/src/elf.rs @@ -202,7 +202,12 @@ impl ElfFile { let segment_start = phent.offset as usize; let segment_end = phent.offset as usize + phent.filesz as usize; let mut segment_data = Vec::from(&bytes[segment_start..segment_end]); - let num_zeroes = (phent.memsz - phent.filesz) as usize; + // If it is not loadable, we do not have any zeroes to represent + let num_zeroes = if phent.type_ != 1 { + 0 + } else { + (phent.memsz - phent.filesz) as usize + }; segment_data.resize(segment_data.len() + num_zeroes, 0); let segment = ElfSegment { @@ -354,4 +359,8 @@ impl ElfFile { )), } } + + pub fn loadable_segments(&self) -> Vec<&ElfSegment> { + self.segments.iter().filter(|s| s.loadable).collect() + } } diff --git a/tool/microkit/src/lib.rs b/tool/microkit/src/lib.rs index 9e163b7f8..c4f81951b 100644 --- a/tool/microkit/src/lib.rs +++ b/tool/microkit/src/lib.rs @@ -114,8 +114,7 @@ impl MemoryRegion { self.end - self.base } - pub fn aligned_power_of_two_regions(&self) -> Vec { - let max_bits = 47; + pub fn aligned_power_of_two_regions(&self, max_bits: u64) -> Vec { let mut regions = Vec::new(); let mut base = self.base; let mut bits; @@ -207,10 +206,10 @@ impl DisjointMemoryRegion { self.check(); } - pub fn aligned_power_of_two_regions(&self) -> Vec { + pub fn aligned_power_of_two_regions(&self, max_bits: u64) -> Vec { let mut aligned_regions = Vec::new(); for region in &self.regions { - aligned_regions.extend(region.aligned_power_of_two_regions()); + aligned_regions.extend(region.aligned_power_of_two_regions(max_bits)); } aligned_regions @@ -218,11 +217,11 @@ impl DisjointMemoryRegion { /// Allocate region of 'size' bytes, returning the base address. /// The allocated region is removed from the disjoint memory region. + /// Allocation policy is simple first fit. + /// Possibly a 'best fit' policy would be better. + /// 'best' may be something that best matches a power-of-two + /// allocation pub fn allocate(&mut self, size: u64) -> u64 { - // Allocation policy is simple first fit. - // Possibly a 'best fit' policy would be better. - // 'best' may be something that best matches a power-of-two - // allocation let mut region_to_remove: Option = None; for region in &self.regions { if size <= region.size() { @@ -239,6 +238,27 @@ impl DisjointMemoryRegion { None => panic!("Unable to allocate {} bytes", size), } } + + pub fn allocate_from(&mut self, size: u64, lower_bound: u64) -> u64 { + let mut region_to_remove = None; + for region in &self.regions { + if size <= region.size() && region.base >= lower_bound { + region_to_remove = Some(*region); + break; + } + } + + match region_to_remove { + Some(region) => { + self.remove_region(region.base, region.base + size); + region.base + } + None => panic!( + "Unable to allocate {} bytes from lower_bound 0x{:x}", + size, lower_bound + ), + } + } } #[derive(Copy, Clone)] diff --git a/tool/microkit/src/loader.rs b/tool/microkit/src/loader.rs index 71e3a4b7b..1ff8fb9b5 100644 --- a/tool/microkit/src/loader.rs +++ b/tool/microkit/src/loader.rs @@ -5,7 +5,7 @@ // use crate::elf::ElfFile; -use crate::sel4::Config; +use crate::sel4::{Arch, Config}; use crate::util::{kb, mask, mb, round_up, struct_to_bytes}; use crate::MemoryRegion; use std::fs::File; @@ -40,6 +40,44 @@ impl Aarch64 { } } +struct Riscv64; +impl Riscv64 { + const BLOCK_BITS_2MB: u64 = 21; + + const PAGE_TABLE_INDEX_BITS: u64 = 9; + const PAGE_SHIFT: u64 = 12; + /// This sets the page table entry bits: D,A,X,W,R. + const PTE_TYPE_BITS: u64 = 0b11001110; + // TODO: where does this come from? + const PTE_TYPE_TABLE: u64 = 0; + const PTE_TYPE_VALID: u64 = 1; + + const PTE_PPN0_SHIFT: u64 = 10; + + /// Due to RISC-V having various virtual memory setups, we have this generic function to + /// figure out the page-table index given the total number of page table levels for the + /// platform and which level we are currently looking at. + pub fn pt_index(pt_levels: usize, addr: u64, level: usize) -> usize { + let pt_index_bits = Self::PAGE_TABLE_INDEX_BITS * (pt_levels - level) as u64; + let idx = (addr >> (pt_index_bits + Self::PAGE_SHIFT)) % 512; + + idx as usize + } + + /// Generate physical page number given an address + pub fn pte_ppn(addr: u64) -> u64 { + (addr >> Self::PAGE_SHIFT) << Self::PTE_PPN0_SHIFT + } + + pub fn pte_next(addr: u64) -> u64 { + Self::pte_ppn(addr) | Self::PTE_TYPE_TABLE | Self::PTE_TYPE_VALID + } + + pub fn pte_leaf(addr: u64) -> u64 { + Self::pte_ppn(addr) | Self::PTE_TYPE_BITS | Self::PTE_TYPE_VALID + } +} + /// Checks that each region in the given list does not overlap with any other region. /// Panics upon finding an overlapping region fn check_non_overlapping(regions: &Vec<(u64, &[u8])>) { @@ -91,7 +129,7 @@ pub struct Loader<'a> { impl<'a> Loader<'a> { pub fn new( - kernel_config: Config, + config: &Config, loader_elf_path: &Path, kernel_elf: &'a ElfFile, initial_task_elf: &'a ElfFile, @@ -154,8 +192,13 @@ impl<'a> Loader<'a> { // (and indeed initial did support multi-segment ELF files). However // it adds significant complexity, and the calling functions enforce // only single-segment ELF files, so we keep things simple here. - assert!(initial_task_elf.segments.len() == 1); - let segment = &initial_task_elf.segments[0]; + let initial_task_segments: Vec<_> = initial_task_elf + .segments + .iter() + .filter(|s| s.loadable) + .collect(); + assert!(initial_task_segments.len() == 1); + let segment = &initial_task_segments[0]; assert!(segment.loadable); let inittask_first_vaddr = segment.virt_addr; @@ -173,11 +216,19 @@ impl<'a> Loader<'a> { // Determine the pagetable variables assert!(kernel_first_vaddr.is_some()); assert!(kernel_first_vaddr.is_some()); - let pagetable_vars = Loader::setup_pagetables( - &elf, - kernel_first_vaddr.unwrap(), - kernel_first_paddr.unwrap(), - ); + let pagetable_vars = match config.arch { + Arch::Aarch64 => Loader::aarch64_setup_pagetables( + &elf, + kernel_first_vaddr.unwrap(), + kernel_first_paddr.unwrap(), + ), + Arch::Riscv64 => Loader::riscv64_setup_pagetables( + config, + &elf, + kernel_first_vaddr.unwrap(), + kernel_first_paddr.unwrap(), + ), + }; let image_segment = elf .segments @@ -221,7 +272,7 @@ impl<'a> Loader<'a> { check_non_overlapping(&all_regions); - let flags = match kernel_config.hypervisor { + let flags = match config.hypervisor { true => 1, false => 0, }; @@ -295,11 +346,85 @@ impl<'a> Loader<'a> { loader_buf.flush().unwrap(); } - fn setup_pagetables( + fn riscv64_setup_pagetables( + config: &Config, + elf: &ElfFile, + first_vaddr: u64, + first_paddr: u64, + ) -> Vec<(u64, u64, [u8; PAGE_TABLE_SIZE])> { + let (text_addr, _) = elf + .find_symbol("_text") + .expect("Could not find 'text' symbol"); + let (boot_lvl1_pt_addr, boot_lvl1_pt_size) = elf + .find_symbol("boot_lvl1_pt") + .expect("Could not find 'boot_lvl1_pt' symbol"); + let (boot_lvl2_pt_addr, boot_lvl2_pt_size) = elf + .find_symbol("boot_lvl2_pt") + .expect("Could not find 'boot_lvl2_pt' symbol"); + let (boot_lvl2_pt_elf_addr, boot_lvl2_pt_elf_size) = elf + .find_symbol("boot_lvl2_pt_elf") + .expect("Could not find 'boot_lvl2_pt_elf' symbol"); + + let num_pt_levels = config.riscv_pt_levels.unwrap().levels(); + + let mut boot_lvl1_pt: [u8; PAGE_TABLE_SIZE] = [0; PAGE_TABLE_SIZE]; + { + let text_index_lvl1 = Riscv64::pt_index(num_pt_levels, text_addr, 1); + let pt_entry = Riscv64::pte_next(boot_lvl2_pt_elf_addr); + let start = 8 * text_index_lvl1; + let end = start + 8; + boot_lvl1_pt[start..end].copy_from_slice(&pt_entry.to_le_bytes()); + } + + let mut boot_lvl2_pt_elf: [u8; PAGE_TABLE_SIZE] = [0; PAGE_TABLE_SIZE]; + { + let text_index_lvl2 = Riscv64::pt_index(num_pt_levels, text_addr, 2); + for (page, i) in (text_index_lvl2..512).enumerate() { + let start = 8 * i; + let end = start + 8; + let addr = text_addr + ((page as u64) << Riscv64::BLOCK_BITS_2MB); + let pt_entry = Riscv64::pte_leaf(addr); + boot_lvl2_pt_elf[start..end].copy_from_slice(&pt_entry.to_le_bytes()); + } + } + + { + let index = Riscv64::pt_index(num_pt_levels, first_vaddr, 1); + let start = 8 * index; + let end = start + 8; + boot_lvl1_pt[start..end] + .copy_from_slice(&Riscv64::pte_next(boot_lvl2_pt_addr).to_le_bytes()); + } + + let mut boot_lvl2_pt: [u8; PAGE_TABLE_SIZE] = [0; PAGE_TABLE_SIZE]; + + { + let index = Riscv64::pt_index(num_pt_levels, first_vaddr, 2); + for (page, i) in (index..512).enumerate() { + let start = 8 * i; + let end = start + 8; + let addr = first_paddr + ((page as u64) << Riscv64::BLOCK_BITS_2MB); + let pt_entry = Riscv64::pte_leaf(addr); + boot_lvl2_pt[start..end].copy_from_slice(&pt_entry.to_le_bytes()); + } + } + + vec![ + (boot_lvl1_pt_addr, boot_lvl1_pt_size, boot_lvl1_pt), + (boot_lvl2_pt_addr, boot_lvl2_pt_size, boot_lvl2_pt), + ( + boot_lvl2_pt_elf_addr, + boot_lvl2_pt_elf_size, + boot_lvl2_pt_elf, + ), + ] + } + + fn aarch64_setup_pagetables( elf: &ElfFile, first_vaddr: u64, first_paddr: u64, - ) -> [(u64, u64, [u8; PAGE_TABLE_SIZE]); 5] { + ) -> Vec<(u64, u64, [u8; PAGE_TABLE_SIZE])> { let (boot_lvl1_lower_addr, boot_lvl1_lower_size) = elf .find_symbol("boot_lvl1_lower") .expect("Could not find 'boot_lvl1_lower' symbol"); @@ -360,7 +485,7 @@ impl<'a> Loader<'a> { boot_lvl2_upper[start..end].copy_from_slice(&pt_entry.to_le_bytes()); } - [ + vec![ (boot_lvl0_lower_addr, boot_lvl0_lower_size, boot_lvl0_lower), (boot_lvl1_lower_addr, boot_lvl1_lower_size, boot_lvl1_lower), (boot_lvl0_upper_addr, boot_lvl0_upper_size, boot_lvl0_upper), diff --git a/tool/microkit/src/main.rs b/tool/microkit/src/main.rs index 994c0a88c..f48e3705d 100644 --- a/tool/microkit/src/main.rs +++ b/tool/microkit/src/main.rs @@ -18,8 +18,9 @@ use sdf::{ SystemDescription, VirtualMachine, }; use sel4::{ - Aarch64Regs, Arch, ArmVmAttributes, BootInfo, Config, Invocation, InvocationArgs, Object, - ObjectType, PageSize, Rights, + default_vm_attr, Aarch64Regs, Arch, ArmVmAttributes, BootInfo, Config, Invocation, + InvocationArgs, Object, ObjectType, PageSize, Rights, Riscv64Regs, RiscvVirtualMemory, + RiscvVmAttributes, }; use std::cmp::{max, min}; use std::collections::{HashMap, HashSet}; @@ -29,7 +30,7 @@ use std::iter::zip; use std::mem::size_of; use std::path::{Path, PathBuf}; use util::{ - bytes_to_struct, comma_sep_u64, comma_sep_usize, json_str_as_bool, json_str_as_u64, + bytes_to_struct, comma_sep_u64, comma_sep_usize, json_str, json_str_as_bool, json_str_as_u64, struct_to_bytes, }; @@ -133,7 +134,7 @@ impl FixedUntypedAlloc { } struct InitSystem<'a> { - kernel_config: &'a Config, + config: &'a Config, cnode_cap: u64, cnode_mask: u64, kao: &'a mut ObjectAllocator, @@ -148,7 +149,7 @@ struct InitSystem<'a> { impl<'a> InitSystem<'a> { #[allow(clippy::too_many_arguments)] // just this one time, pinky-promise... pub fn new( - kernel_config: &'a Config, + config: &'a Config, cnode_cap: u64, cnode_mask: u64, first_available_cap_slot: u64, @@ -171,7 +172,7 @@ impl<'a> InitSystem<'a> { device_untyped.sort_by(|a, b| a.ut.base().cmp(&b.ut.base())); InitSystem { - kernel_config, + config, cnode_cap, cnode_mask, kao: kernel_object_allocator, @@ -220,11 +221,11 @@ impl<'a> InitSystem<'a> { names: Vec, ) -> Vec { assert!(phys_address >= self.last_fixed_address); - assert!(object_type.fixed_size().is_some()); + assert!(object_type.fixed_size(self.config).is_some()); assert!(count == names.len() as u64); assert!(count > 0); - let alloc_size = object_type.fixed_size().unwrap(); + let alloc_size = object_type.fixed_size(self.config).unwrap(); // Find an untyped that contains the given address let fut: &mut FixedUntypedAlloc = self .device_untyped @@ -270,8 +271,9 @@ impl<'a> InitSystem<'a> { } for sz in padding_sizes { - self.invocations - .push(Invocation::new(InvocationArgs::UntypedRetype { + self.invocations.push(Invocation::new( + self.config, + InvocationArgs::UntypedRetype { untyped: fut.ut.cap, object_type: ObjectType::Untyped, size_bits: sz.ilog2() as u64, @@ -280,15 +282,17 @@ impl<'a> InitSystem<'a> { node_depth: 1, node_offset: self.cap_slot, num_objects: 1, - })); + }, + )); self.cap_slot += 1; } } let object_cap = self.cap_slot; self.cap_slot += 1; - self.invocations - .push(Invocation::new(InvocationArgs::UntypedRetype { + self.invocations.push(Invocation::new( + self.config, + InvocationArgs::UntypedRetype { untyped: fut.ut.cap, object_type, size_bits: 0, @@ -297,7 +301,8 @@ impl<'a> InitSystem<'a> { node_depth: 1, node_offset: object_cap, num_objects: 1, - })); + }, + )); fut.watermark = phys_address + alloc_size; self.last_fixed_address = phys_address + alloc_size; @@ -330,7 +335,7 @@ impl<'a> InitSystem<'a> { let alloc_size; let api_size: u64; - if let Some(object_size) = object_type.fixed_size() { + if let Some(object_size) = object_type.fixed_size(self.config) { // An object with a fixed size should not be allocated with a given size assert!(size.is_none()); alloc_size = object_size; @@ -351,9 +356,10 @@ impl<'a> InitSystem<'a> { let mut to_alloc = count; let mut alloc_cap_slot = base_cap_slot; while to_alloc > 0 { - let call_count = min(to_alloc, self.kernel_config.fan_out_limit); - self.invocations - .push(Invocation::new(InvocationArgs::UntypedRetype { + let call_count = min(to_alloc, self.config.fan_out_limit); + self.invocations.push(Invocation::new( + self.config, + InvocationArgs::UntypedRetype { untyped: allocation.untyped_cap_address, object_type, size_bits: api_size, @@ -362,7 +368,8 @@ impl<'a> InitSystem<'a> { node_depth: 1, node_offset: alloc_cap_slot, num_objects: call_count, - })); + }, + )); to_alloc -= call_count; alloc_cap_slot += call_count; } @@ -421,6 +428,7 @@ fn phys_mem_regions_from_elf(elf: &ElfFile, alignment: u64) -> Vec elf.segments .iter() + .filter(|s| s.loadable) .map(|s| { MemoryRegion::new( util::round_down(s.phys_addr, alignment), @@ -436,7 +444,7 @@ fn phys_mem_regions_from_elf(elf: &ElfFile, alignment: u64) -> Vec /// segment, and returns the region covering the first segment. fn phys_mem_region_from_elf(elf: &ElfFile, alignment: u64) -> MemoryRegion { assert!(alignment > 0); - assert!(elf.segments.len() == 1); + assert!(elf.segments.iter().filter(|s| s.loadable).count() == 1); phys_mem_regions_from_elf(elf, alignment)[0] } @@ -450,6 +458,7 @@ fn virt_mem_regions_from_elf(elf: &ElfFile, alignment: u64) -> Vec assert!(alignment > 0); elf.segments .iter() + .filter(|s| s.loadable) .map(|s| { MemoryRegion::new( util::round_down(s.virt_addr, alignment), @@ -465,7 +474,7 @@ fn virt_mem_regions_from_elf(elf: &ElfFile, alignment: u64) -> Vec /// segment, and returns the region covering the first segment. fn virt_mem_region_from_elf(elf: &ElfFile, alignment: u64) -> MemoryRegion { assert!(alignment > 0); - assert!(elf.segments.len() == 1); + assert!(elf.segments.iter().filter(|s| s.loadable).count() == 1); virt_mem_regions_from_elf(elf, alignment)[0] } @@ -489,31 +498,52 @@ struct KernelPartialBootInfo { // Corresponds to kernel_frame_t in the kernel #[repr(C)] -struct KernelFrame64 { +struct KernelFrameRiscv64 { pub paddr: u64, pub pptr: u64, - pub execute_never: u32, - pub user_accessible: u32, + pub user_accessible: i32, } -fn kernel_device_addrs(kernel_config: &Config, kernel_elf: &ElfFile) -> Vec { - assert!(kernel_config.word_size == 64, "Unsupported word-size"); +#[repr(C)] +struct KernelFrameAarch64 { + pub paddr: u64, + pub pptr: u64, + pub execute_never: i32, + pub user_accessible: i32, +} + +fn kernel_device_addrs(config: &Config, kernel_elf: &ElfFile) -> Vec { + assert!(config.word_size == 64, "Unsupported word-size"); let mut kernel_devices = Vec::new(); let (vaddr, size) = kernel_elf .find_symbol("kernel_device_frames") .expect("Could not find 'kernel_device_frames' symbol"); let kernel_frame_bytes = kernel_elf.get_data(vaddr, size).unwrap(); - let kernel_frame_size = size_of::(); + let kernel_frame_size = match config.arch { + Arch::Aarch64 => size_of::(), + Arch::Riscv64 => size_of::(), + }; let mut offset: usize = 0; while offset < size as usize { - let kernel_frame = unsafe { - bytes_to_struct::( - &kernel_frame_bytes[offset..offset + kernel_frame_size], - ) + let (user_accessible, paddr) = unsafe { + match config.arch { + Arch::Aarch64 => { + let frame = bytes_to_struct::( + &kernel_frame_bytes[offset..offset + kernel_frame_size], + ); + (frame.user_accessible, frame.paddr) + } + Arch::Riscv64 => { + let frame = bytes_to_struct::( + &kernel_frame_bytes[offset..offset + kernel_frame_size], + ); + (frame.user_accessible, frame.paddr) + } + } }; - if kernel_frame.user_accessible == 0 { - kernel_devices.push(kernel_frame.paddr); + if user_accessible == 0 { + kernel_devices.push(paddr); } offset += kernel_frame_size; } @@ -549,21 +579,23 @@ fn kernel_phys_mem(kernel_config: &Config, kernel_elf: &ElfFile) -> Vec<(u64, u6 } fn kernel_self_mem(kernel_elf: &ElfFile) -> MemoryRegion { - let base = kernel_elf.segments[0].phys_addr; + let segments = kernel_elf.loadable_segments(); + let base = segments[0].phys_addr; let (ki_end_v, _) = kernel_elf .find_symbol("ki_end") .expect("Could not find 'ki_end' symbol"); - let ki_end_p = ki_end_v - kernel_elf.segments[0].virt_addr + base; + let ki_end_p = ki_end_v - segments[0].virt_addr + base; MemoryRegion::new(base, ki_end_p) } fn kernel_boot_mem(kernel_elf: &ElfFile) -> MemoryRegion { - let base = kernel_elf.segments[0].phys_addr; + let segments = kernel_elf.loadable_segments(); + let base = segments[0].phys_addr; let (ki_boot_end_v, _) = kernel_elf .find_symbol("ki_boot_end") .expect("Could not find 'ki_boot_end' symbol"); - let ki_boot_end_p = ki_boot_end_v - kernel_elf.segments[0].virt_addr + base; + let ki_boot_end_p = ki_boot_end_v - segments[0].virt_addr + base; MemoryRegion::new(base, ki_boot_end_p) } @@ -618,9 +650,9 @@ fn kernel_partial_boot(kernel_config: &Config, kernel_elf: &ElfFile) -> KernelPa fn emulate_kernel_boot_partial( kernel_config: &Config, kernel_elf: &ElfFile, -) -> DisjointMemoryRegion { +) -> (DisjointMemoryRegion, MemoryRegion) { let partial_info = kernel_partial_boot(kernel_config, kernel_elf); - partial_info.normal_memory + (partial_info.normal_memory, partial_info.boot_region) } fn get_n_paging(region: MemoryRegion, bits: u64) -> u64 { @@ -630,35 +662,47 @@ fn get_n_paging(region: MemoryRegion, bits: u64) -> u64 { (end - start) / (1 << bits) } -fn get_arch_n_paging(region: MemoryRegion) -> u64 { - const PT_INDEX_OFFSET: u64 = 12; - const PD_INDEX_OFFSET: u64 = PT_INDEX_OFFSET + 9; - const PUD_INDEX_OFFSET: u64 = PD_INDEX_OFFSET + 9; +fn get_arch_n_paging(config: &Config, region: MemoryRegion) -> u64 { + match config.arch { + Arch::Aarch64 => { + const PT_INDEX_OFFSET: u64 = 12; + const PD_INDEX_OFFSET: u64 = PT_INDEX_OFFSET + 9; + const PUD_INDEX_OFFSET: u64 = PD_INDEX_OFFSET + 9; - get_n_paging(region, PUD_INDEX_OFFSET) + get_n_paging(region, PD_INDEX_OFFSET) + get_n_paging(region, PUD_INDEX_OFFSET) + get_n_paging(region, PD_INDEX_OFFSET) + } + Arch::Riscv64 => match config.riscv_pt_levels.unwrap() { + RiscvVirtualMemory::Sv39 => { + const PT_INDEX_OFFSET: u64 = 12; + const LVL1_INDEX_OFFSET: u64 = PT_INDEX_OFFSET + 9; + const LVL2_INDEX_OFFSET: u64 = LVL1_INDEX_OFFSET + 9; + + get_n_paging(region, LVL2_INDEX_OFFSET) + get_n_paging(region, LVL1_INDEX_OFFSET) + } + }, + } } -fn rootserver_max_size_bits() -> u64 { +fn rootserver_max_size_bits(config: &Config) -> u64 { let slot_bits = 5; // seL4_SlotBits - let root_cnode_bits = 12; // CONFIG_ROOT_CNODE_SIZE_BITS - let vspace_bits = 13; // seL4_VSpaceBits + let root_cnode_bits = config.init_cnode_bits; // CONFIG_ROOT_CNODE_SIZE_BITS + let vspace_bits = ObjectType::VSpace.fixed_size_bits(config).unwrap(); let cnode_size_bits = root_cnode_bits + slot_bits; max(cnode_size_bits, vspace_bits) } -fn calculate_rootserver_size(initial_task_region: MemoryRegion) -> u64 { +fn calculate_rootserver_size(config: &Config, initial_task_region: MemoryRegion) -> u64 { // FIXME: These constants should ideally come from the config / kernel // binary not be hard coded here. // But they are constant so it isn't too bad. - // This is specifically for aarch64 let slot_bits = 5; // seL4_SlotBits - let root_cnode_bits = 12; // CONFIG_ROOT_CNODE_SIZE_BITS - let tcb_bits = 11; // seL4_TCBBits - let page_bits = 12; // seL4_PageBits + let root_cnode_bits = config.init_cnode_bits; // CONFIG_ROOT_CNODE_SIZE_BITS + let tcb_bits = ObjectType::Tcb.fixed_size_bits(config).unwrap(); // seL4_TCBBits + let page_bits = ObjectType::SmallPage.fixed_size_bits(config).unwrap(); // seL4_PageBits let asid_pool_bits = 12; // seL4_ASIDPoolBits - let vspace_bits = 13; // seL4_VSpaceBits - let page_table_bits = 12; // seL4_PageTableBits + let vspace_bits = ObjectType::VSpace.fixed_size_bits(config).unwrap(); // seL4_VSpaceBits + let page_table_bits = ObjectType::PageTable.fixed_size_bits(config).unwrap(); // seL4_PageTableBits let min_sched_context_bits = 7; // seL4_MinSchedContextBits let mut size = 0; @@ -667,7 +711,7 @@ fn calculate_rootserver_size(initial_task_region: MemoryRegion) -> u64 { size += 2 * (1 << page_bits); size += 1 << asid_pool_bits; size += 1 << vspace_bits; - size += get_arch_n_paging(initial_task_region) * (1 << page_table_bits); + size += get_arch_n_paging(config, initial_task_region) * (1 << page_table_bits); size += 1 << min_sched_context_bits; size @@ -676,14 +720,14 @@ fn calculate_rootserver_size(initial_task_region: MemoryRegion) -> u64 { /// Emulate what happens during a kernel boot, generating a /// representation of the BootInfo struct. fn emulate_kernel_boot( - kernel_config: &Config, + config: &Config, kernel_elf: &ElfFile, initial_task_phys_region: MemoryRegion, initial_task_virt_region: MemoryRegion, reserved_region: MemoryRegion, ) -> BootInfo { assert!(initial_task_phys_region.size() == initial_task_virt_region.size()); - let partial_info = kernel_partial_boot(kernel_config, kernel_elf); + let partial_info = kernel_partial_boot(config, kernel_elf); let mut normal_memory = partial_info.normal_memory; let device_memory = partial_info.device_memory; let boot_region = partial_info.boot_region; @@ -692,8 +736,8 @@ fn emulate_kernel_boot( normal_memory.remove_region(reserved_region.base, reserved_region.end); // Now, the tricky part! determine which memory is used for the initial task objects - let initial_objects_size = calculate_rootserver_size(initial_task_virt_region); - let initial_objects_align = rootserver_max_size_bits(); + let initial_objects_size = calculate_rootserver_size(config, initial_task_virt_region); + let initial_objects_align = rootserver_max_size_bits(config); // Find an appropriate region of normal memory to allocate the objects // from; this follows the same algorithm used within the kernel boot code @@ -718,20 +762,24 @@ fn emulate_kernel_boot( let fixed_cap_count = 0x10; let sched_control_cap_count = 1; - let paging_cap_count = get_arch_n_paging(initial_task_virt_region); - let page_cap_count = initial_task_virt_region.size() / kernel_config.minimum_page_size; + let paging_cap_count = get_arch_n_paging(config, initial_task_virt_region); + let page_cap_count = initial_task_virt_region.size() / config.minimum_page_size; let first_untyped_cap = fixed_cap_count + paging_cap_count + sched_control_cap_count + page_cap_count; let sched_control_cap = fixed_cap_count + paging_cap_count; + let max_bits = match config.arch { + Arch::Aarch64 => 47, + Arch::Riscv64 => 38, + }; let device_regions: Vec = [ - reserved_region.aligned_power_of_two_regions(), - device_memory.aligned_power_of_two_regions(), + reserved_region.aligned_power_of_two_regions(max_bits), + device_memory.aligned_power_of_two_regions(max_bits), ] .concat(); let normal_regions: Vec = [ - boot_region.aligned_power_of_two_regions(), - normal_memory.aligned_power_of_two_regions(), + boot_region.aligned_power_of_two_regions(max_bits), + normal_memory.aligned_power_of_two_regions(max_bits), ] .concat(); let mut untyped_objects = Vec::new(); @@ -758,7 +806,7 @@ fn emulate_kernel_boot( } fn build_system( - kernel_config: &Config, + config: &Config, kernel_elf: &ElfFile, monitor_elf: &ElfFile, system: &SystemDescription, @@ -767,7 +815,7 @@ fn build_system( search_paths: &Vec, ) -> Result { assert!(util::is_power_of_two(system_cnode_size)); - assert!(invocation_table_size % kernel_config.minimum_page_size == 0); + assert!(invocation_table_size % config.minimum_page_size == 0); assert!(invocation_table_size <= MAX_SYSTEM_INVOCATION_SIZE); let mut cap_address_names: HashMap = HashMap::new(); @@ -783,8 +831,7 @@ fn build_system( // Emulate kernel boot // Determine physical memory region used by the monitor - let initial_task_size = - phys_mem_region_from_elf(monitor_elf, kernel_config.minimum_page_size).size(); + let initial_task_size = phys_mem_region_from_elf(monitor_elf, config.minimum_page_size).size(); // Get the elf files for each pd: let mut pd_elf_files = Vec::with_capacity(system.protection_domains.len()); @@ -811,7 +858,7 @@ fn build_system( // protection domains let mut pd_elf_size = 0; for pd_elf in &pd_elf_files { - for r in phys_mem_regions_from_elf(pd_elf, kernel_config.minimum_page_size) { + for r in phys_mem_regions_from_elf(pd_elf, config.minimum_page_size) { pd_elf_size += r.size(); } } @@ -819,20 +866,26 @@ fn build_system( // Now that the size is determined, find a free region in the physical memory // space. - let mut available_memory = emulate_kernel_boot_partial(kernel_config, kernel_elf); - - let reserved_base = available_memory.allocate(reserved_size); - let initial_task_phys_base = available_memory.allocate(initial_task_size); - // The kernel relies on this ordering. The previous allocation functions do *NOT* enforce - // this though, should fix that. + let (mut available_memory, kernel_boot_region) = + emulate_kernel_boot_partial(config, kernel_elf); + + // The kernel relies on the reserved region being allocated above the kernel + // boot/ELF region, so we have the end of the kernel boot region as the lower + // bound for allocating the reserved region. + let reserved_base = available_memory.allocate_from(reserved_size, kernel_boot_region.end); + assert!(kernel_boot_region.base < reserved_base); + // The kernel relies on the initial task being allocated above the reserved + // region, so we have the address of the end of the reserved region as the + // lower bound for allocating the initial task. + let initial_task_phys_base = + available_memory.allocate_from(initial_task_size, reserved_base + reserved_size); assert!(reserved_base < initial_task_phys_base); let initial_task_phys_region = MemoryRegion::new( initial_task_phys_base, initial_task_phys_base + initial_task_size, ); - let initial_task_virt_region = - virt_mem_region_from_elf(monitor_elf, kernel_config.minimum_page_size); + let initial_task_virt_region = virt_mem_region_from_elf(monitor_elf, config.minimum_page_size); let reserved_region = MemoryRegion::new(reserved_base, reserved_base + reserved_size); @@ -846,7 +899,7 @@ fn build_system( // boot can be emulated. This provides the boot info information which is needed // for the next steps let kernel_boot_info = emulate_kernel_boot( - kernel_config, + config, kernel_elf, initial_task_phys_region, initial_task_virt_region, @@ -919,16 +972,19 @@ fn build_system( // First up create the root cnode let mut bootstrap_invocations = Vec::new(); - bootstrap_invocations.push(Invocation::new(InvocationArgs::UntypedRetype { - untyped: root_cnode_allocation.untyped_cap_address, - object_type: ObjectType::CNode, - size_bits: root_cnode_bits, - root: INIT_CNODE_CAP_ADDRESS, - node_index: 0, - node_depth: 0, - node_offset: root_cnode_cap, - num_objects: 1, - })); + bootstrap_invocations.push(Invocation::new( + config, + InvocationArgs::UntypedRetype { + untyped: root_cnode_allocation.untyped_cap_address, + object_type: ObjectType::CNode, + size_bits: root_cnode_bits, + root: INIT_CNODE_CAP_ADDRESS, + node_index: 0, + node_depth: 0, + node_offset: root_cnode_cap, + num_objects: 1, + }, + )); // 2.1.4: Now insert a cap to the initial Cnode into slot zero of the newly // allocated root Cnode. It uses sufficient guard bits to ensure it is @@ -936,58 +992,70 @@ fn build_system( // // guard size is the lower bit of the guard, upper bits are the guard itself // which for out purposes is always zero. - let guard = kernel_config.cap_address_bits - root_cnode_bits - kernel_config.init_cnode_bits; - bootstrap_invocations.push(Invocation::new(InvocationArgs::CnodeMint { - cnode: root_cnode_cap, - dest_index: 0, - dest_depth: root_cnode_bits, - src_root: INIT_CNODE_CAP_ADDRESS, - src_obj: INIT_CNODE_CAP_ADDRESS, - src_depth: kernel_config.cap_address_bits, - rights: Rights::All as u64, - badge: guard, - })); + let guard = config.cap_address_bits - root_cnode_bits - config.init_cnode_bits; + bootstrap_invocations.push(Invocation::new( + config, + InvocationArgs::CnodeMint { + cnode: root_cnode_cap, + dest_index: 0, + dest_depth: root_cnode_bits, + src_root: INIT_CNODE_CAP_ADDRESS, + src_obj: INIT_CNODE_CAP_ADDRESS, + src_depth: config.cap_address_bits, + rights: Rights::All as u64, + badge: guard, + }, + )); // 2.1.5: Now it is possible to switch our root Cnode to the newly create // root cnode. We have a zero sized guard. This Cnode represents the top // bit of any cap addresses. let root_guard = 0; - bootstrap_invocations.push(Invocation::new(InvocationArgs::TcbSetSpace { - tcb: INIT_TCB_CAP_ADDRESS, - fault_ep: INIT_NULL_CAP_ADDRESS, - cspace_root: root_cnode_cap, - cspace_root_data: root_guard, - vspace_root: INIT_VSPACE_CAP_ADDRESS, - vspace_root_data: 0, - })); + bootstrap_invocations.push(Invocation::new( + config, + InvocationArgs::TcbSetSpace { + tcb: INIT_TCB_CAP_ADDRESS, + fault_ep: INIT_NULL_CAP_ADDRESS, + cspace_root: root_cnode_cap, + cspace_root_data: root_guard, + vspace_root: INIT_VSPACE_CAP_ADDRESS, + vspace_root_data: 0, + }, + )); // 2.1.6: Now we can create our new system Cnode. We will place it into // a temporary cap slot in the initial CNode to start with. - bootstrap_invocations.push(Invocation::new(InvocationArgs::UntypedRetype { - untyped: system_cnode_allocation.untyped_cap_address, - object_type: ObjectType::CNode, - size_bits: system_cnode_bits, - root: INIT_CNODE_CAP_ADDRESS, - node_index: 0, - node_depth: 0, - node_offset: system_cnode_cap, - num_objects: 1, - })); + bootstrap_invocations.push(Invocation::new( + config, + InvocationArgs::UntypedRetype { + untyped: system_cnode_allocation.untyped_cap_address, + object_type: ObjectType::CNode, + size_bits: system_cnode_bits, + root: INIT_CNODE_CAP_ADDRESS, + node_index: 0, + node_depth: 0, + node_offset: system_cnode_cap, + num_objects: 1, + }, + )); // 2.1.7: Now that the we have create the object, we can 'mutate' it // to the correct place: // Slot #1 of the new root cnode - let system_cap_address_mask = 1 << (kernel_config.cap_address_bits - 1); - bootstrap_invocations.push(Invocation::new(InvocationArgs::CnodeMint { - cnode: root_cnode_cap, - dest_index: 1, - dest_depth: root_cnode_bits, - src_root: INIT_CNODE_CAP_ADDRESS, - src_obj: system_cnode_cap, - src_depth: kernel_config.cap_address_bits, - rights: Rights::All as u64, - badge: kernel_config.cap_address_bits - root_cnode_bits - system_cnode_bits, - })); + let system_cap_address_mask = 1 << (config.cap_address_bits - 1); + bootstrap_invocations.push(Invocation::new( + config, + InvocationArgs::CnodeMint { + cnode: root_cnode_cap, + dest_index: 1, + dest_depth: root_cnode_bits, + src_root: INIT_CNODE_CAP_ADDRESS, + src_obj: system_cnode_cap, + src_depth: config.cap_address_bits, + rights: Rights::All as u64, + badge: config.cap_address_bits - root_cnode_bits - system_cnode_bits, + }, + )); // 2.2 At this point it is necessary to get the frames containing the // main system invocations into the virtual address space. (Remember the @@ -1009,7 +1077,7 @@ fn build_system( // page size. It would be good in the future to use super pages (when // it makes sense to - this would reduce memory usage, and the number of // invocations required to set up the address space - let pages_required = invocation_table_size / kernel_config.minimum_page_size; + let pages_required = invocation_table_size / config.minimum_page_size; let base_page_cap = 0; for pta in base_page_cap..base_page_cap + pages_required { cap_address_names.insert( @@ -1029,23 +1097,26 @@ fn build_system( .filter(|o| o.is_device) .collect(); for ut in boot_info_device_untypeds { - let ut_pages = ut.region.size() / kernel_config.minimum_page_size; + let ut_pages = ut.region.size() / config.minimum_page_size; let retype_page_count = min(ut_pages, remaining_pages); - assert!(retype_page_count <= kernel_config.fan_out_limit); - bootstrap_invocations.push(Invocation::new(InvocationArgs::UntypedRetype { - untyped: ut.cap, - object_type: ObjectType::SmallPage, - size_bits: 0, - root: root_cnode_cap, - node_index: 1, - node_depth: 1, - node_offset: cap_slot, - num_objects: retype_page_count, - })); + assert!(retype_page_count <= config.fan_out_limit); + bootstrap_invocations.push(Invocation::new( + config, + InvocationArgs::UntypedRetype { + untyped: ut.cap, + object_type: ObjectType::SmallPage, + size_bits: 0, + root: root_cnode_cap, + node_index: 1, + node_depth: 1, + node_offset: cap_slot, + num_objects: retype_page_count, + }, + )); remaining_pages -= retype_page_count; cap_slot += retype_page_count; - phys_addr += retype_page_count * kernel_config.minimum_page_size; + phys_addr += retype_page_count * config.minimum_page_size; invocation_table_allocations.push((ut, phys_addr)); if remaining_pages == 0 { break; @@ -1060,9 +1131,11 @@ fn build_system( // sized system. // // Before mapping it is necessary to install page tables that can cover the region. - let page_tables_required = util::round_up(invocation_table_size, sel4::OBJECT_SIZE_LARGE_PAGE) - / sel4::OBJECT_SIZE_LARGE_PAGE; - let page_table_allocation = kao.alloc_n(sel4::OBJECT_SIZE_PAGE_TABLE, page_tables_required); + let large_page_size = ObjectType::LargePage.fixed_size(config).unwrap(); + let page_table_size = ObjectType::PageTable.fixed_size(config).unwrap(); + let page_tables_required = + util::round_up(invocation_table_size, large_page_size) / large_page_size; + let page_table_allocation = kao.alloc_n(page_table_size, page_tables_required); let base_page_table_cap = cap_slot; for pta in base_page_table_cap..base_page_table_cap + page_tables_required { @@ -1072,27 +1145,37 @@ fn build_system( ); } - assert!(page_tables_required <= kernel_config.fan_out_limit); - bootstrap_invocations.push(Invocation::new(InvocationArgs::UntypedRetype { - untyped: page_table_allocation.untyped_cap_address, - object_type: ObjectType::PageTable, - size_bits: 0, - root: root_cnode_cap, - node_index: 1, - node_depth: 1, - node_offset: cap_slot, - num_objects: page_tables_required, - })); + assert!(page_tables_required <= config.fan_out_limit); + bootstrap_invocations.push(Invocation::new( + config, + InvocationArgs::UntypedRetype { + untyped: page_table_allocation.untyped_cap_address, + object_type: ObjectType::PageTable, + size_bits: 0, + root: root_cnode_cap, + node_index: 1, + node_depth: 1, + node_offset: cap_slot, + num_objects: page_tables_required, + }, + )); cap_slot += page_tables_required; let page_table_vaddr: u64 = 0x8000_0000; // Now that the page tables are allocated they can be mapped into vspace - let mut pt_map_invocation = Invocation::new(InvocationArgs::PageTableMap { - page_table: system_cap_address_mask | base_page_table_cap, - vspace: INIT_VSPACE_CAP_ADDRESS, - vaddr: page_table_vaddr, - attr: ArmVmAttributes::default(), - }); + let bootstrap_pt_attr = match config.arch { + Arch::Aarch64 => ArmVmAttributes::default(), + Arch::Riscv64 => RiscvVmAttributes::default(), + }; + let mut pt_map_invocation = Invocation::new( + config, + InvocationArgs::PageTableMap { + page_table: system_cap_address_mask | base_page_table_cap, + vspace: INIT_VSPACE_CAP_ADDRESS, + vaddr: page_table_vaddr, + attr: bootstrap_pt_attr, + }, + ); pt_map_invocation.repeat( page_tables_required as u32, InvocationArgs::PageTableMap { @@ -1106,19 +1189,26 @@ fn build_system( // Finally, once the page tables are allocated the pages can be mapped let page_vaddr: u64 = 0x8000_0000; - let mut map_invocation = Invocation::new(InvocationArgs::PageMap { - page: system_cap_address_mask | base_page_cap, - vspace: INIT_VSPACE_CAP_ADDRESS, - vaddr: page_vaddr, - rights: Rights::Read as u64, - attr: ArmVmAttributes::default() | ArmVmAttributes::ExecuteNever as u64, - }); + let bootstrap_page_attr = match config.arch { + Arch::Aarch64 => ArmVmAttributes::default() | ArmVmAttributes::ExecuteNever as u64, + Arch::Riscv64 => RiscvVmAttributes::default() | RiscvVmAttributes::ExecuteNever as u64, + }; + let mut map_invocation = Invocation::new( + config, + InvocationArgs::PageMap { + page: system_cap_address_mask | base_page_cap, + vspace: INIT_VSPACE_CAP_ADDRESS, + vaddr: page_vaddr, + rights: Rights::Read as u64, + attr: bootstrap_page_attr, + }, + ); map_invocation.repeat( pages_required as u32, InvocationArgs::PageMap { page: 1, vspace: 0, - vaddr: kernel_config.minimum_page_size, + vaddr: config.minimum_page_size, rights: 0, attr: 0, }, @@ -1154,8 +1244,7 @@ fn build_system( continue; } - let segment_phys_addr = - phys_addr_next + (segment.virt_addr % kernel_config.minimum_page_size); + let segment_phys_addr = phys_addr_next + (segment.virt_addr % config.minimum_page_size); pd_elf_regions[i].push(Region::new( format!("PD-ELF {}-{}", pd.name, seg_idx), segment_phys_addr, @@ -1174,10 +1263,10 @@ fn build_system( perms |= SysMapPerms::Execute as u8; } - let base_vaddr = util::round_down(segment.virt_addr, kernel_config.minimum_page_size); + let base_vaddr = util::round_down(segment.virt_addr, config.minimum_page_size); let end_vaddr = util::round_up( segment.virt_addr + segment.mem_size(), - kernel_config.minimum_page_size, + config.minimum_page_size, ); let aligned_size = end_vaddr - base_vaddr; let name = format!("ELF:{}-{}", pd.name, seg_idx); @@ -1221,7 +1310,7 @@ fn build_system( phys_addr: None, }; - let stack_vaddr = kernel_config.user_top(); + let stack_vaddr = config.user_top(); let stack_map = SysMap { mr: stack_mr.name.clone(), @@ -1247,7 +1336,7 @@ fn build_system( let mut system_invocations: Vec = Vec::new(); let mut init_system = InitSystem::new( - kernel_config, + config, root_cnode_cap, system_cap_address_mask, cap_slot, @@ -1491,9 +1580,15 @@ fn build_system( } for (vaddr, page_size) in vaddrs { - if !kernel_config.hypervisor && kernel_config.arm_pa_size_bits != 40 { - upper_directory_vaddrs.insert(util::mask_bits(vaddr, 12 + 9 + 9 + 9)); + match config.arch { + Arch::Aarch64 => { + if !config.hypervisor && config.arm_pa_size_bits.unwrap() != 40 { + upper_directory_vaddrs.insert(util::mask_bits(vaddr, 12 + 9 + 9 + 9)); + } + } + Arch::Riscv64 => {} } + directory_vaddrs.insert(util::mask_bits(vaddr, 12 + 9 + 9)); if page_size == PageSize::Small { page_table_vaddrs.insert(util::mask_bits(vaddr, 12 + 9)); @@ -1544,8 +1639,8 @@ fn build_system( } for (vaddr, page_size) in vaddrs { - assert!(kernel_config.hypervisor); - if kernel_config.arm_pa_size_bits != 40 { + assert!(config.hypervisor); + if config.arm_pa_size_bits.unwrap() != 40 { upper_directory_vaddrs.insert(util::mask_bits(vaddr, 12 + 9 + 9 + 9)); } directory_vaddrs.insert(util::mask_bits(vaddr, 12 + 9 + 9)); @@ -1612,7 +1707,7 @@ fn build_system( let pd_ud_objs = init_system.allocate_objects(ObjectType::PageTable, pd_ud_names, None); let vm_ud_objs = init_system.allocate_objects(ObjectType::PageTable, vm_ud_names, None); - if kernel_config.hypervisor { + if config.hypervisor { assert!(vm_ud_objs.is_empty()); } @@ -1668,14 +1763,17 @@ fn build_system( irq_cap_addresses.insert(pd, vec![]); for sysirq in &pd.irqs { let cap_address = system_cap_address_mask | cap_slot; - system_invocations.push(Invocation::new(InvocationArgs::IrqControlGetTrigger { - irq_control: IRQ_CONTROL_CAP_ADDRESS, - irq: sysirq.irq, - trigger: sysirq.trigger, - dest_root: root_cnode_cap, - dest_index: cap_address, - dest_depth: kernel_config.cap_address_bits, - })); + system_invocations.push(Invocation::new( + config, + InvocationArgs::IrqControlGetTrigger { + irq_control: IRQ_CONTROL_CAP_ADDRESS, + irq: sysirq.irq, + trigger: sysirq.trigger, + dest_root: root_cnode_cap, + dest_index: cap_address, + dest_depth: config.cap_address_bits, + }, + )); cap_slot += 1; cap_address_names.insert(cap_address, format!("IRQ Handler: irq={}", sysirq.irq)); @@ -1685,10 +1783,13 @@ fn build_system( // This has to be done prior to minting! let num_asid_invocations = system.protection_domains.len() + virtual_machines.len(); - let mut asid_invocation = Invocation::new(InvocationArgs::AsidPoolAssign { - asid_pool: INIT_ASID_POOL_CAP_ADDRESS, - vspace: vspace_objs[0].cap_addr, - }); + let mut asid_invocation = Invocation::new( + config, + InvocationArgs::AsidPoolAssign { + asid_pool: INIT_ASID_POOL_CAP_ADDRESS, + vspace: vspace_objs[0].cap_addr, + }, + ); asid_invocation.repeat( num_asid_invocations as u32, InvocationArgs::AsidPoolAssign { @@ -1708,7 +1809,10 @@ fn build_system( for mp in map_set { let mr = all_mr_by_name[mp.mr.as_str()]; let mut rights: u64 = Rights::None as u64; - let mut attrs = ArmVmAttributes::ParityEnabled as u64; + let mut attrs = match config.arch { + Arch::Aarch64 => ArmVmAttributes::ParityEnabled as u64, + Arch::Riscv64 => 0, + }; if mp.perms & SysMapPerms::Read as u8 != 0 { rights |= Rights::Read as u64; } @@ -1716,25 +1820,34 @@ fn build_system( rights |= Rights::Write as u64; } if mp.perms & SysMapPerms::Execute as u8 == 0 { - attrs |= ArmVmAttributes::ExecuteNever as u64; + match config.arch { + Arch::Aarch64 => attrs |= ArmVmAttributes::ExecuteNever as u64, + Arch::Riscv64 => attrs |= RiscvVmAttributes::ExecuteNever as u64, + } } if mp.cached { - attrs |= ArmVmAttributes::Cacheable as u64; + match config.arch { + Arch::Aarch64 => attrs |= ArmVmAttributes::Cacheable as u64, + Arch::Riscv64 => {} + } } assert!(!mr_pages[mr].is_empty()); assert!(util::objects_adjacent(&mr_pages[mr])); - let mut invocation = Invocation::new(InvocationArgs::CnodeMint { - cnode: system_cnode_cap, - dest_index: cap_slot, - dest_depth: system_cnode_bits, - src_root: root_cnode_cap, - src_obj: mr_pages[mr][0].cap_addr, - src_depth: kernel_config.cap_address_bits, - rights, - badge: 0, - }); + let mut invocation = Invocation::new( + config, + InvocationArgs::CnodeMint { + cnode: system_cnode_cap, + dest_index: cap_slot, + dest_depth: system_cnode_bits, + src_root: root_cnode_cap, + src_obj: mr_pages[mr][0].cap_addr, + src_depth: config.cap_address_bits, + rights, + badge: 0, + }, + ); invocation.repeat( mr_pages[mr].len() as u32, InvocationArgs::CnodeMint { @@ -1782,7 +1895,10 @@ fn build_system( for mp in &vm.maps { let mr = all_mr_by_name[mp.mr.as_str()]; let mut rights: u64 = Rights::None as u64; - let mut attrs = ArmVmAttributes::ParityEnabled as u64; + let mut attrs = match config.arch { + Arch::Aarch64 => ArmVmAttributes::ParityEnabled as u64, + Arch::Riscv64 => 0, + }; if mp.perms & SysMapPerms::Read as u8 != 0 { rights |= Rights::Read as u64; } @@ -1790,25 +1906,34 @@ fn build_system( rights |= Rights::Write as u64; } if mp.perms & SysMapPerms::Execute as u8 == 0 { - attrs |= ArmVmAttributes::ExecuteNever as u64; + match config.arch { + Arch::Aarch64 => attrs |= ArmVmAttributes::ExecuteNever as u64, + Arch::Riscv64 => attrs |= RiscvVmAttributes::ExecuteNever as u64, + } } if mp.cached { - attrs |= ArmVmAttributes::Cacheable as u64; + match config.arch { + Arch::Aarch64 => attrs |= ArmVmAttributes::Cacheable as u64, + Arch::Riscv64 => {} + } } assert!(!mr_pages[mr].is_empty()); assert!(util::objects_adjacent(&mr_pages[mr])); - let mut invocation = Invocation::new(InvocationArgs::CnodeMint { - cnode: system_cnode_cap, - dest_index: cap_slot, - dest_depth: system_cnode_bits, - src_root: root_cnode_cap, - src_obj: mr_pages[mr][0].cap_addr, - src_depth: kernel_config.cap_address_bits, - rights, - badge: 0, - }); + let mut invocation = Invocation::new( + config, + InvocationArgs::CnodeMint { + cnode: system_cnode_cap, + dest_index: cap_slot, + dest_depth: system_cnode_bits, + src_root: root_cnode_cap, + src_obj: mr_pages[mr][0].cap_addr, + src_depth: config.cap_address_bits, + rights, + badge: 0, + }, + ); invocation.repeat( mr_pages[mr].len() as u32, InvocationArgs::CnodeMint { @@ -1856,16 +1981,19 @@ fn build_system( for sysirq in &pd.irqs { let badge = 1 << sysirq.id; let badged_cap_address = system_cap_address_mask | cap_slot; - system_invocations.push(Invocation::new(InvocationArgs::CnodeMint { - cnode: system_cnode_cap, - dest_index: cap_slot, - dest_depth: system_cnode_bits, - src_root: root_cnode_cap, - src_obj: notification_obj.cap_addr, - src_depth: kernel_config.cap_address_bits, - rights: Rights::All as u64, - badge, - })); + system_invocations.push(Invocation::new( + config, + InvocationArgs::CnodeMint { + cnode: system_cnode_cap, + dest_index: cap_slot, + dest_depth: system_cnode_bits, + src_root: root_cnode_cap, + src_obj: notification_obj.cap_addr, + src_depth: config.cap_address_bits, + rights: Rights::All as u64, + badge, + }, + )); let badged_name = format!( "{} (badge=0x{:x})", cap_address_names[¬ification_obj.cap_addr], badge @@ -1897,16 +2025,19 @@ fn build_system( badge = FAULT_BADGE | pd.id.unwrap(); } - let invocation = Invocation::new(InvocationArgs::CnodeMint { - cnode: system_cnode_cap, - dest_index: cap_slot, - dest_depth: system_cnode_bits, - src_root: root_cnode_cap, - src_obj: fault_ep_cap, - src_depth: kernel_config.cap_address_bits, - rights: Rights::All as u64, - badge, - }); + let invocation = Invocation::new( + config, + InvocationArgs::CnodeMint { + cnode: system_cnode_cap, + dest_index: cap_slot, + dest_depth: system_cnode_bits, + src_root: root_cnode_cap, + src_obj: fault_ep_cap, + src_depth: config.cap_address_bits, + rights: Rights::All as u64, + badge, + }, + ); system_invocations.push(invocation); cap_slot += 1; } @@ -1928,16 +2059,19 @@ fn build_system( let fault_ep_cap = pd_endpoint_objs[parent_pd.unwrap()].unwrap().cap_addr; let badge = FAULT_BADGE | vm.vcpu.id; - let invocation = Invocation::new(InvocationArgs::CnodeMint { - cnode: system_cnode_cap, - dest_index: cap_slot, - dest_depth: system_cnode_bits, - src_root: root_cnode_cap, - src_obj: fault_ep_cap, - src_depth: kernel_config.cap_address_bits, - rights: Rights::All as u64, - badge, - }); + let invocation = Invocation::new( + config, + InvocationArgs::CnodeMint { + cnode: system_cnode_cap, + dest_index: cap_slot, + dest_depth: system_cnode_bits, + src_root: root_cnode_cap, + src_obj: fault_ep_cap, + src_depth: config.cap_address_bits, + rights: Rights::All as u64, + badge, + }, + ); system_invocations.push(invocation); cap_slot += 1; } @@ -1953,30 +2087,36 @@ fn build_system( }; assert!(INPUT_CAP_IDX < PD_CAP_SIZE); - system_invocations.push(Invocation::new(InvocationArgs::CnodeMint { - cnode: cnode_objs[idx].cap_addr, - dest_index: INPUT_CAP_IDX, - dest_depth: PD_CAP_BITS, - src_root: root_cnode_cap, - src_obj: obj.cap_addr, - src_depth: kernel_config.cap_address_bits, - rights: Rights::All as u64, - badge: 0, - })); + system_invocations.push(Invocation::new( + config, + InvocationArgs::CnodeMint { + cnode: cnode_objs[idx].cap_addr, + dest_index: INPUT_CAP_IDX, + dest_depth: PD_CAP_BITS, + src_root: root_cnode_cap, + src_obj: obj.cap_addr, + src_depth: config.cap_address_bits, + rights: Rights::All as u64, + badge: 0, + }, + )); } // Mint access to the reply cap assert!(REPLY_CAP_IDX < PD_CAP_SIZE); - let mut reply_mint_invocation = Invocation::new(InvocationArgs::CnodeMint { - cnode: cnode_objs[0].cap_addr, - dest_index: REPLY_CAP_IDX, - dest_depth: PD_CAP_BITS, - src_root: root_cnode_cap, - src_obj: pd_reply_objs[0].cap_addr, - src_depth: kernel_config.cap_address_bits, - rights: Rights::All as u64, - badge: 1, - }); + let mut reply_mint_invocation = Invocation::new( + config, + InvocationArgs::CnodeMint { + cnode: cnode_objs[0].cap_addr, + dest_index: REPLY_CAP_IDX, + dest_depth: PD_CAP_BITS, + src_root: root_cnode_cap, + src_obj: pd_reply_objs[0].cap_addr, + src_depth: config.cap_address_bits, + rights: Rights::All as u64, + badge: 1, + }, + ); reply_mint_invocation.repeat( system.protection_domains.len() as u32, InvocationArgs::CnodeMint { @@ -1995,16 +2135,19 @@ fn build_system( // Mint access to the VSpace cap assert!(VSPACE_CAP_IDX < PD_CAP_SIZE); let num_vspace_mint_invocations = system.protection_domains.len() + virtual_machines.len(); - let mut vspace_mint_invocation = Invocation::new(InvocationArgs::CnodeMint { - cnode: cnode_objs[0].cap_addr, - dest_index: VSPACE_CAP_IDX, - dest_depth: PD_CAP_BITS, - src_root: root_cnode_cap, - src_obj: vspace_objs[0].cap_addr, - src_depth: kernel_config.cap_address_bits, - rights: Rights::All as u64, - badge: 0, - }); + let mut vspace_mint_invocation = Invocation::new( + config, + InvocationArgs::CnodeMint { + cnode: cnode_objs[0].cap_addr, + dest_index: VSPACE_CAP_IDX, + dest_depth: PD_CAP_BITS, + src_root: root_cnode_cap, + src_obj: vspace_objs[0].cap_addr, + src_depth: config.cap_address_bits, + rights: Rights::All as u64, + badge: 0, + }, + ); vspace_mint_invocation.repeat( num_vspace_mint_invocations as u32, InvocationArgs::CnodeMint { @@ -2025,16 +2168,19 @@ fn build_system( for (sysirq, irq_cap_address) in zip(&pd.irqs, &irq_cap_addresses[pd]) { let cap_idx = BASE_IRQ_CAP + sysirq.id; assert!(cap_idx < PD_CAP_SIZE); - system_invocations.push(Invocation::new(InvocationArgs::CnodeMint { - cnode: cnode_objs[pd_idx].cap_addr, - dest_index: cap_idx, - dest_depth: PD_CAP_BITS, - src_root: root_cnode_cap, - src_obj: *irq_cap_address, - src_depth: kernel_config.cap_address_bits, - rights: Rights::All as u64, - badge: 0, - })); + system_invocations.push(Invocation::new( + config, + InvocationArgs::CnodeMint { + cnode: cnode_objs[pd_idx].cap_addr, + dest_index: cap_idx, + dest_depth: PD_CAP_BITS, + src_root: root_cnode_cap, + src_obj: *irq_cap_address, + src_depth: config.cap_address_bits, + rights: Rights::All as u64, + badge: 0, + }, + )); } } @@ -2048,16 +2194,19 @@ fn build_system( if parent_idx == pd_idx { let cap_idx = BASE_PD_TCB_CAP + maybe_child_pd.id.unwrap(); assert!(cap_idx < PD_CAP_SIZE); - system_invocations.push(Invocation::new(InvocationArgs::CnodeMint { - cnode: cnode_objs[pd_idx].cap_addr, - dest_index: cap_idx, - dest_depth: PD_CAP_BITS, - src_root: root_cnode_cap, - src_obj: tcb_objs[maybe_child_idx].cap_addr, - src_depth: kernel_config.cap_address_bits, - rights: Rights::All as u64, - badge: 0, - })); + system_invocations.push(Invocation::new( + config, + InvocationArgs::CnodeMint { + cnode: cnode_objs[pd_idx].cap_addr, + dest_index: cap_idx, + dest_depth: PD_CAP_BITS, + src_root: root_cnode_cap, + src_obj: tcb_objs[maybe_child_idx].cap_addr, + src_depth: config.cap_address_bits, + rights: Rights::All as u64, + badge: 0, + }, + )); } } } @@ -2071,16 +2220,19 @@ fn build_system( let vm_idx = virtual_machines.iter().position(|&x| x == vm).unwrap(); let cap_idx = BASE_VM_TCB_CAP + vm.vcpu.id; assert!(cap_idx < PD_CAP_SIZE); - system_invocations.push(Invocation::new(InvocationArgs::CnodeMint { - cnode: cnode_objs[pd_idx].cap_addr, - dest_index: cap_idx, - dest_depth: PD_CAP_BITS, - src_root: root_cnode_cap, - src_obj: vm_tcb_objs[vm_idx].cap_addr, - src_depth: kernel_config.cap_address_bits, - rights: Rights::All as u64, - badge: 0, - })); + system_invocations.push(Invocation::new( + config, + InvocationArgs::CnodeMint { + cnode: cnode_objs[pd_idx].cap_addr, + dest_index: cap_idx, + dest_depth: PD_CAP_BITS, + src_root: root_cnode_cap, + src_obj: vm_tcb_objs[vm_idx].cap_addr, + src_depth: config.cap_address_bits, + rights: Rights::All as u64, + badge: 0, + }, + )); } } @@ -2092,16 +2244,19 @@ fn build_system( let vm_idx = virtual_machines.iter().position(|&x| x == vm).unwrap(); let cap_idx = BASE_VCPU_CAP + vm.vcpu.id; assert!(cap_idx < PD_CAP_SIZE); - system_invocations.push(Invocation::new(InvocationArgs::CnodeMint { - cnode: cnode_objs[pd_idx].cap_addr, - dest_index: cap_idx, - dest_depth: PD_CAP_BITS, - src_root: root_cnode_cap, - src_obj: vcpu_objs[vm_idx].cap_addr, - src_depth: kernel_config.cap_address_bits, - rights: Rights::All as u64, - badge: 0, - })); + system_invocations.push(Invocation::new( + config, + InvocationArgs::CnodeMint { + cnode: cnode_objs[pd_idx].cap_addr, + dest_index: cap_idx, + dest_depth: PD_CAP_BITS, + src_root: root_cnode_cap, + src_obj: vcpu_objs[vm_idx].cap_addr, + src_depth: config.cap_address_bits, + rights: Rights::All as u64, + badge: 0, + }, + )); } } @@ -2117,30 +2272,36 @@ fn build_system( let pd_a_cap_idx = BASE_OUTPUT_NOTIFICATION_CAP + cc.id_a; let pd_a_badge = 1 << cc.id_b; assert!(pd_a_cap_idx < PD_CAP_SIZE); - system_invocations.push(Invocation::new(InvocationArgs::CnodeMint { - cnode: pd_a_cnode_obj.cap_addr, - dest_index: pd_a_cap_idx, - dest_depth: PD_CAP_BITS, - src_root: root_cnode_cap, - src_obj: pd_b_notification_obj.cap_addr, - src_depth: kernel_config.cap_address_bits, - rights: Rights::All as u64, // FIXME: Check rights - badge: pd_a_badge, - })); + system_invocations.push(Invocation::new( + config, + InvocationArgs::CnodeMint { + cnode: pd_a_cnode_obj.cap_addr, + dest_index: pd_a_cap_idx, + dest_depth: PD_CAP_BITS, + src_root: root_cnode_cap, + src_obj: pd_b_notification_obj.cap_addr, + src_depth: config.cap_address_bits, + rights: Rights::All as u64, // FIXME: Check rights + badge: pd_a_badge, + }, + )); let pd_b_cap_idx = BASE_OUTPUT_NOTIFICATION_CAP + cc.id_b; let pd_b_badge = 1 << cc.id_a; assert!(pd_b_cap_idx < PD_CAP_SIZE); - system_invocations.push(Invocation::new(InvocationArgs::CnodeMint { - cnode: pd_b_cnode_obj.cap_addr, - dest_index: pd_b_cap_idx, - dest_depth: PD_CAP_BITS, - src_root: root_cnode_cap, - src_obj: pd_a_notification_obj.cap_addr, - src_depth: kernel_config.cap_address_bits, - rights: Rights::All as u64, // FIXME: Check rights - badge: pd_b_badge, - })); + system_invocations.push(Invocation::new( + config, + InvocationArgs::CnodeMint { + cnode: pd_b_cnode_obj.cap_addr, + dest_index: pd_b_cap_idx, + dest_depth: PD_CAP_BITS, + src_root: root_cnode_cap, + src_obj: pd_a_notification_obj.cap_addr, + src_depth: config.cap_address_bits, + rights: Rights::All as u64, // FIXME: Check rights + badge: pd_b_badge, + }, + )); // Set up the endpoint caps if pd_b.pp { @@ -2149,16 +2310,19 @@ fn build_system( let pd_b_endpoint_obj = pd_endpoint_objs[cc.pd_b].unwrap(); assert!(pd_a_cap_idx < PD_CAP_SIZE); - system_invocations.push(Invocation::new(InvocationArgs::CnodeMint { - cnode: pd_a_cnode_obj.cap_addr, - dest_index: pd_a_cap_idx, - dest_depth: PD_CAP_BITS, - src_root: root_cnode_cap, - src_obj: pd_b_endpoint_obj.cap_addr, - src_depth: kernel_config.cap_address_bits, - rights: Rights::All as u64, // FIXME: Check rights - badge: pd_a_badge, - })); + system_invocations.push(Invocation::new( + config, + InvocationArgs::CnodeMint { + cnode: pd_a_cnode_obj.cap_addr, + dest_index: pd_a_cap_idx, + dest_depth: PD_CAP_BITS, + src_root: root_cnode_cap, + src_obj: pd_b_endpoint_obj.cap_addr, + src_depth: config.cap_address_bits, + rights: Rights::All as u64, // FIXME: Check rights + badge: pd_a_badge, + }, + )); } if pd_a.pp { @@ -2167,16 +2331,19 @@ fn build_system( let pd_a_endpoint_obj = pd_endpoint_objs[cc.pd_a].unwrap(); assert!(pd_b_cap_idx < PD_CAP_SIZE); - system_invocations.push(Invocation::new(InvocationArgs::CnodeMint { - cnode: pd_b_cnode_obj.cap_addr, - dest_index: pd_b_cap_idx, - dest_depth: PD_CAP_BITS, - src_root: root_cnode_cap, - src_obj: pd_a_endpoint_obj.cap_addr, - src_depth: kernel_config.cap_address_bits, - rights: Rights::All as u64, // FIXME: Check rights - badge: pd_b_badge, - })); + system_invocations.push(Invocation::new( + config, + InvocationArgs::CnodeMint { + cnode: pd_b_cnode_obj.cap_addr, + dest_index: pd_b_cap_idx, + dest_depth: PD_CAP_BITS, + src_root: root_cnode_cap, + src_obj: pd_a_endpoint_obj.cap_addr, + src_depth: config.cap_address_bits, + rights: Rights::All as u64, // FIXME: Check rights + badge: pd_b_badge, + }, + )); } } @@ -2184,17 +2351,20 @@ fn build_system( for (pd_idx, pd) in system.protection_domains.iter().enumerate() { if pd.passive { let cnode_obj = &cnode_objs[pd_idx]; - system_invocations.push(Invocation::new(InvocationArgs::CnodeMint { - cnode: cnode_obj.cap_addr, - dest_index: MONITOR_EP_CAP_IDX, - dest_depth: PD_CAP_BITS, - src_root: root_cnode_cap, - src_obj: fault_ep_endpoint_object.cap_addr, - src_depth: kernel_config.cap_address_bits, - rights: Rights::All as u64, // FIXME: Check rights - // Badge needs to start at 1 - badge: pd_idx as u64 + 1, - })); + system_invocations.push(Invocation::new( + config, + InvocationArgs::CnodeMint { + cnode: cnode_obj.cap_addr, + dest_index: MONITOR_EP_CAP_IDX, + dest_depth: PD_CAP_BITS, + src_root: root_cnode_cap, + src_obj: fault_ep_endpoint_object.cap_addr, + src_depth: config.cap_address_bits, + rights: Rights::All as u64, // FIXME: Check rights + // Badge needs to start at 1 + badge: pd_idx as u64 + 1, + }, + )); } } @@ -2206,64 +2376,73 @@ fn build_system( for (irq_cap_address, badged_notification_cap_address) in zip(&irq_cap_addresses[pd], &badged_irq_caps[pd]) { - system_invocations.push(Invocation::new(InvocationArgs::IrqHandlerSetNotification { - irq_handler: *irq_cap_address, - notification: *badged_notification_cap_address, - })); + system_invocations.push(Invocation::new( + config, + InvocationArgs::IrqHandlerSetNotification { + irq_handler: *irq_cap_address, + notification: *badged_notification_cap_address, + }, + )); } } // Initialise the VSpaces -- assign them all the the initial asid pool. - let pd_vspace_invocations = if kernel_config.hypervisor && kernel_config.arm_pa_size_bits == 40 - { - vec![(all_pd_ds, pd_d_objs), (all_pd_pts, pd_pt_objs)] - } else { - vec![ - (all_pd_uds, pd_ud_objs), - (all_pd_ds, pd_d_objs), - (all_pd_pts, pd_pt_objs), - ] - }; + let pd_vspace_invocations = [ + (all_pd_uds, pd_ud_objs), + (all_pd_ds, pd_d_objs), + (all_pd_pts, pd_pt_objs), + ]; for (descriptors, objects) in pd_vspace_invocations { for ((pd_idx, vaddr), obj) in zip(descriptors, objects) { - system_invocations.push(Invocation::new(InvocationArgs::PageTableMap { - page_table: obj.cap_addr, - vspace: pd_vspace_objs[pd_idx].cap_addr, - vaddr, - attr: ArmVmAttributes::default(), - })); + system_invocations.push(Invocation::new( + config, + InvocationArgs::PageTableMap { + page_table: obj.cap_addr, + vspace: pd_vspace_objs[pd_idx].cap_addr, + vaddr, + attr: default_vm_attr(config), + }, + )); } } - let vm_vspace_invocations = if kernel_config.hypervisor && kernel_config.arm_pa_size_bits == 40 - { - vec![(all_vm_ds, vm_d_objs), (all_vm_pts, vm_pt_objs)] - } else { - vec![ - (all_vm_uds, vm_ud_objs), - (all_vm_ds, vm_d_objs), - (all_vm_pts, vm_pt_objs), - ] - }; + + if config.hypervisor { + assert!(all_vm_uds.is_empty() && vm_ud_objs.is_empty()); + assert!(all_vm_ds.is_empty() && vm_d_objs.is_empty()); + assert!(all_vm_pts.is_empty() && vm_pt_objs.is_empty()); + } + + let vm_vspace_invocations = [ + (all_vm_uds, vm_ud_objs), + (all_vm_ds, vm_d_objs), + (all_vm_pts, vm_pt_objs), + ]; for (descriptors, objects) in vm_vspace_invocations { for ((vm_idx, vaddr), obj) in zip(descriptors, objects) { - system_invocations.push(Invocation::new(InvocationArgs::PageTableMap { - page_table: obj.cap_addr, - vspace: vm_vspace_objs[vm_idx].cap_addr, - vaddr, - attr: ArmVmAttributes::default(), - })); + system_invocations.push(Invocation::new( + config, + InvocationArgs::PageTableMap { + page_table: obj.cap_addr, + vspace: vm_vspace_objs[vm_idx].cap_addr, + vaddr, + attr: default_vm_attr(config), + }, + )); } } // Now map all the pages for (page_cap_address, pd_idx, vaddr, rights, attr, count, vaddr_incr) in pd_page_descriptors { - let mut invocation = Invocation::new(InvocationArgs::PageMap { - page: page_cap_address, - vspace: pd_vspace_objs[pd_idx].cap_addr, - vaddr, - rights, - attr, - }); + let mut invocation = Invocation::new( + config, + InvocationArgs::PageMap { + page: page_cap_address, + vspace: pd_vspace_objs[pd_idx].cap_addr, + vaddr, + rights, + attr, + }, + ); invocation.repeat( count as u32, InvocationArgs::PageMap { @@ -2277,13 +2456,16 @@ fn build_system( system_invocations.push(invocation); } for (page_cap_address, vm_idx, vaddr, rights, attr, count, vaddr_incr) in vm_page_descriptors { - let mut invocation = Invocation::new(InvocationArgs::PageMap { - page: page_cap_address, - vspace: vm_vspace_objs[vm_idx].cap_addr, - vaddr, - rights, - attr, - }); + let mut invocation = Invocation::new( + config, + InvocationArgs::PageMap { + page: page_cap_address, + vspace: vm_vspace_objs[vm_idx].cap_addr, + vaddr, + rights, + attr, + }, + ); invocation.repeat( count as u32, InvocationArgs::PageMap { @@ -2298,17 +2480,24 @@ fn build_system( } // And, finally, map all the IPC buffers + let ipc_buffer_attr = match config.arch { + Arch::Aarch64 => ArmVmAttributes::default() | ArmVmAttributes::ExecuteNever as u64, + Arch::Riscv64 => RiscvVmAttributes::default() | RiscvVmAttributes::ExecuteNever as u64, + }; for pd_idx in 0..system.protection_domains.len() { let (vaddr, _) = pd_elf_files[pd_idx] .find_symbol(SYMBOL_IPC_BUFFER) .unwrap_or_else(|_| panic!("Could not find {}", SYMBOL_IPC_BUFFER)); - system_invocations.push(Invocation::new(InvocationArgs::PageMap { - page: ipc_buffer_objs[pd_idx].cap_addr, - vspace: pd_vspace_objs[pd_idx].cap_addr, - vaddr, - rights: Rights::Read as u64 | Rights::Write as u64, - attr: ArmVmAttributes::default() | ArmVmAttributes::ExecuteNever as u64, - })); + system_invocations.push(Invocation::new( + config, + InvocationArgs::PageMap { + page: ipc_buffer_objs[pd_idx].cap_addr, + vspace: pd_vspace_objs[pd_idx].cap_addr, + vaddr, + rights: Rights::Read as u64 | Rights::Write as u64, + attr: ipc_buffer_attr, + }, + )); } // Initialise the TCBs @@ -2316,6 +2505,7 @@ fn build_system( // Set the scheduling parameters for (pd_idx, pd) in system.protection_domains.iter().enumerate() { system_invocations.push(Invocation::new( + config, InvocationArgs::SchedControlConfigureFlags { sched_control: kernel_boot_info.sched_control_cap, sched_context: pd_sched_context_objs[pd_idx].cap_addr, @@ -2329,6 +2519,7 @@ fn build_system( } for (vm_idx, vm) in virtual_machines.iter().enumerate() { system_invocations.push(Invocation::new( + config, InvocationArgs::SchedControlConfigureFlags { sched_control: kernel_boot_info.sched_control_cap, sched_context: vm_sched_context_objs[vm_idx].cap_addr, @@ -2342,40 +2533,49 @@ fn build_system( } for (pd_idx, pd) in system.protection_domains.iter().enumerate() { - system_invocations.push(Invocation::new(InvocationArgs::TcbSetSchedParams { - tcb: pd_tcb_objs[pd_idx].cap_addr, - authority: INIT_TCB_CAP_ADDRESS, - mcp: pd.priority as u64, - priority: pd.priority as u64, - sched_context: pd_sched_context_objs[pd_idx].cap_addr, - // This gets over-written by the call to TCB_SetSpace - fault_ep: fault_ep_endpoint_object.cap_addr, - })); + system_invocations.push(Invocation::new( + config, + InvocationArgs::TcbSetSchedParams { + tcb: pd_tcb_objs[pd_idx].cap_addr, + authority: INIT_TCB_CAP_ADDRESS, + mcp: pd.priority as u64, + priority: pd.priority as u64, + sched_context: pd_sched_context_objs[pd_idx].cap_addr, + // This gets over-written by the call to TCB_SetSpace + fault_ep: fault_ep_endpoint_object.cap_addr, + }, + )); } for (vm_idx, vm) in virtual_machines.iter().enumerate() { - system_invocations.push(Invocation::new(InvocationArgs::TcbSetSchedParams { - tcb: vm_tcb_objs[vm_idx].cap_addr, - authority: INIT_TCB_CAP_ADDRESS, - mcp: vm.priority as u64, - priority: vm.priority as u64, - sched_context: vm_sched_context_objs[vm_idx].cap_addr, - // This gets over-written by the call to TCB_SetSpace - fault_ep: fault_ep_endpoint_object.cap_addr, - })); + system_invocations.push(Invocation::new( + config, + InvocationArgs::TcbSetSchedParams { + tcb: vm_tcb_objs[vm_idx].cap_addr, + authority: INIT_TCB_CAP_ADDRESS, + mcp: vm.priority as u64, + priority: vm.priority as u64, + sched_context: vm_sched_context_objs[vm_idx].cap_addr, + // This gets over-written by the call to TCB_SetSpace + fault_ep: fault_ep_endpoint_object.cap_addr, + }, + )); } // In the benchmark configuration, we allow PDs to access their own TCB. // This is necessary for accessing kernel's benchmark API. - if kernel_config.benchmark { - let mut tcb_cap_copy_invocation = Invocation::new(InvocationArgs::CnodeCopy { - cnode: cnode_objs[0].cap_addr, - dest_index: TCB_CAP_IDX, - dest_depth: PD_CAP_BITS, - src_root: root_cnode_cap, - src_obj: pd_tcb_objs[0].cap_addr, - src_depth: kernel_config.cap_address_bits, - rights: Rights::All as u64, - }); + if config.benchmark { + let mut tcb_cap_copy_invocation = Invocation::new( + config, + InvocationArgs::CnodeCopy { + cnode: cnode_objs[0].cap_addr, + dest_index: TCB_CAP_IDX, + dest_depth: PD_CAP_BITS, + src_root: root_cnode_cap, + src_obj: pd_tcb_objs[0].cap_addr, + src_depth: config.cap_address_bits, + rights: Rights::All as u64, + }, + ); tcb_cap_copy_invocation.repeat( system.protection_domains.len() as u32, InvocationArgs::CnodeCopy { @@ -2393,14 +2593,17 @@ fn build_system( // Set VSpace and CSpace let num_set_space_invocations = system.protection_domains.len() + virtual_machines.len(); - let mut set_space_invocation = Invocation::new(InvocationArgs::TcbSetSpace { - tcb: tcb_objs[0].cap_addr, - fault_ep: badged_fault_ep, - cspace_root: cnode_objs[0].cap_addr, - cspace_root_data: kernel_config.cap_address_bits - PD_CAP_BITS, - vspace_root: vspace_objs[0].cap_addr, - vspace_root_data: 0, - }); + let mut set_space_invocation = Invocation::new( + config, + InvocationArgs::TcbSetSpace { + tcb: tcb_objs[0].cap_addr, + fault_ep: badged_fault_ep, + cspace_root: cnode_objs[0].cap_addr, + cspace_root_data: config.cap_address_bits - PD_CAP_BITS, + vspace_root: vspace_objs[0].cap_addr, + vspace_root_data: 0, + }, + ); set_space_invocation.repeat( num_set_space_invocations as u32, InvocationArgs::TcbSetSpace { @@ -2419,37 +2622,55 @@ fn build_system( let (ipc_buffer_vaddr, _) = pd_elf_files[pd_idx] .find_symbol(SYMBOL_IPC_BUFFER) .unwrap_or_else(|_| panic!("Could not find {}", SYMBOL_IPC_BUFFER)); - system_invocations.push(Invocation::new(InvocationArgs::TcbSetIpcBuffer { - tcb: tcb_objs[pd_idx].cap_addr, - buffer: ipc_buffer_vaddr, - buffer_frame: ipc_buffer_objs[pd_idx].cap_addr, - })); + system_invocations.push(Invocation::new( + config, + InvocationArgs::TcbSetIpcBuffer { + tcb: tcb_objs[pd_idx].cap_addr, + buffer: ipc_buffer_vaddr, + buffer_frame: ipc_buffer_objs[pd_idx].cap_addr, + }, + )); } // Set TCB registers (we only set the entry point) for pd_idx in 0..system.protection_domains.len() { - let regs = Aarch64Regs { - pc: pd_elf_files[pd_idx].entry, - sp: kernel_config.user_top(), - ..Default::default() + let regs = match config.arch { + Arch::Aarch64 => Aarch64Regs { + pc: pd_elf_files[pd_idx].entry, + sp: config.user_top(), + ..Default::default() + } + .field_names(), + Arch::Riscv64 => Riscv64Regs { + pc: pd_elf_files[pd_idx].entry, + sp: config.user_top(), + ..Default::default() + } + .field_names(), }; - system_invocations.push(Invocation::new(InvocationArgs::TcbWriteRegisters { - tcb: tcb_objs[pd_idx].cap_addr, - resume: false, - // There are no arch-dependent flags to set - arch_flags: 0, - // FIXME: we could optimise this since we are only setting the program counter - count: Aarch64Regs::LEN as u64, - regs, - })); + system_invocations.push(Invocation::new( + config, + InvocationArgs::TcbWriteRegisters { + tcb: tcb_objs[pd_idx].cap_addr, + resume: false, + // There are no arch-dependent flags to set + arch_flags: 0, + // FIXME: we could optimise this since we are only setting the program counter + count: regs.len() as u64, + regs, + }, + )); } // Bind the notification object - let mut bind_ntfn_invocation = Invocation::new(InvocationArgs::TcbBindNotification { - tcb: tcb_objs[0].cap_addr, - notification: notification_objs[0].cap_addr, - }); + let mut bind_ntfn_invocation = Invocation::new( + config, + InvocationArgs::TcbBindNotification { + tcb: tcb_objs[0].cap_addr, + notification: notification_objs[0].cap_addr, + }, + ); bind_ntfn_invocation.repeat( system.protection_domains.len() as u32, InvocationArgs::TcbBindNotification { @@ -2461,10 +2682,17 @@ fn build_system( // Bind virtual machine TCBs to vCPUs if !virtual_machines.is_empty() { - let mut vcpu_bind_invocation = Invocation::new(InvocationArgs::ArmVcpuSetTcb { - vcpu: vcpu_objs[0].cap_addr, - tcb: vm_tcb_objs[0].cap_addr, - }); + match config.arch { + Arch::Aarch64 => {} + _ => panic!("Support for virtual machines is only for AArch64"), + } + let mut vcpu_bind_invocation = Invocation::new( + config, + InvocationArgs::ArmVcpuSetTcb { + vcpu: vcpu_objs[0].cap_addr, + tcb: vm_tcb_objs[0].cap_addr, + }, + ); vcpu_bind_invocation.repeat( virtual_machines.len() as u32, InvocationArgs::ArmVcpuSetTcb { vcpu: 1, tcb: 1 }, @@ -2473,9 +2701,12 @@ fn build_system( } // Resume (start) all the threads that belong to PDs (VMs are not started upon system init) - let mut resume_invocation = Invocation::new(InvocationArgs::TcbResume { - tcb: tcb_objs[0].cap_addr, - }); + let mut resume_invocation = Invocation::new( + config, + InvocationArgs::TcbResume { + tcb: tcb_objs[0].cap_addr, + }, + ); resume_invocation.repeat( system.protection_domains.len() as u32, InvocationArgs::TcbResume { tcb: 1 }, @@ -2489,7 +2720,7 @@ fn build_system( let mut system_invocation_data: Vec = Vec::new(); for system_invocation in &system_invocations { - system_invocation.add_raw_invocation(&mut system_invocation_data); + system_invocation.add_raw_invocation(config, &mut system_invocation_data); } for (i, pd) in system.protection_domains.iter().enumerate() { @@ -2556,6 +2787,7 @@ fn build_system( fn write_report( buf: &mut BufWriter, + config: &Config, built_system: &BuiltSystem, bootstrap_invocation_data: &[u8], ) -> std::io::Result<()> { @@ -2632,18 +2864,21 @@ fn write_report( writeln!( buf, " {:<50} {} cap_addr={:x} phys_addr={:x}", - ko.name, ko.object_type as u64, ko.cap_addr, ko.phys_addr + ko.name, + ko.object_type.value(config), + ko.cap_addr, + ko.phys_addr )?; } writeln!(buf, "\n# Bootstrap Kernel Invocations Detail\n")?; for (i, invocation) in built_system.bootstrap_invocations.iter().enumerate() { write!(buf, " 0x{:04x} ", i)?; - invocation.report_fmt(buf, &built_system.cap_lookup); + invocation.report_fmt(buf, config, &built_system.cap_lookup); } writeln!(buf, "\n# System Kernel Invocations Detail\n")?; for (i, invocation) in built_system.system_invocations.iter().enumerate() { write!(buf, " 0x{:04x} ", i)?; - invocation.report_fmt(buf, &built_system.cap_lookup); + invocation.report_fmt(buf, config, &built_system.cap_lookup); } Ok(()) @@ -2938,35 +3173,67 @@ fn main() -> Result<(), String> { let kernel_config_json: serde_json::Value = serde_json::from_str(&fs::read_to_string(kernel_config_path).unwrap()).unwrap(); + let arch = match json_str(&kernel_config_json, "SEL4_ARCH")? { + "aarch64" => Arch::Aarch64, + "riscv64" => Arch::Riscv64, + _ => panic!("Unsupported kernel config architecture"), + }; + + let hypervisor = match arch { + Arch::Aarch64 => json_str_as_bool(&kernel_config_json, "ARM_HYPERVISOR_SUPPORT")?, + // Hypervisor mode is not available on RISC-V + Arch::Riscv64 => false, + }; + + let arm_pa_size_bits = match arch { + Arch::Aarch64 => { + if json_str_as_bool(&kernel_config_json, "ARM_PA_SIZE_BITS_40")? { + Some(40) + } else if json_str_as_bool(&kernel_config_json, "ARM_PA_SIZE_BITS_44")? { + Some(44) + } else { + panic!("Expected ARM platform to have 40 or 44 physical address bits") + } + } + Arch::Riscv64 => None, + }; + + let kernel_frame_size = match arch { + Arch::Aarch64 => 1 << 12, + Arch::Riscv64 => 1 << 21, + }; + let kernel_config = Config { - arch: Arch::Aarch64, + arch, word_size: json_str_as_u64(&kernel_config_json, "WORD_SIZE")?, minimum_page_size: 4096, paddr_user_device_top: json_str_as_u64(&kernel_config_json, "PADDR_USER_DEVICE_TOP")?, - kernel_frame_size: 1 << 12, + kernel_frame_size, init_cnode_bits: json_str_as_u64(&kernel_config_json, "ROOT_CNODE_SIZE_BITS")?, cap_address_bits: 64, fan_out_limit: json_str_as_u64(&kernel_config_json, "RETYPE_FAN_OUT_LIMIT")?, - arm_pa_size_bits: 40, - hypervisor: json_str_as_bool(&kernel_config_json, "ARM_HYPERVISOR_SUPPORT")?, + hypervisor, benchmark: args.config == "benchmark", + fpu: json_str_as_bool(&kernel_config_json, "HAVE_FPU")?, + arm_pa_size_bits, + riscv_pt_levels: Some(RiscvVirtualMemory::Sv39), }; - match kernel_config.arch { - Arch::Aarch64 => assert!( + if let Arch::Aarch64 = kernel_config.arch { + assert!( kernel_config.hypervisor, "Microkit tool expects a kernel with hypervisor mode enabled on AArch64." - ), + ); + assert!( + kernel_config.arm_pa_size_bits.unwrap() == 40, + "Microkit tool has assumptions about the ARM physical address size bits" + ); } assert!( kernel_config.word_size == 64, "Microkit tool has various assumptions about the word size being 64-bits." ); - assert!( - kernel_config.arm_pa_size_bits == 40, - "Microkit tool has assumptions about the ARM physical address size bits" - ); let plat_desc = PlatformDescription::new(&kernel_config); let system = match parse(args.system, &xml, &plat_desc) { @@ -2987,7 +3254,7 @@ fn main() -> Result<(), String> { let kernel_elf = ElfFile::from_path(&kernel_elf_path)?; let mut monitor_elf = ElfFile::from_path(&monitor_elf_path)?; - if monitor_elf.segments.len() > 1 { + if monitor_elf.segments.iter().filter(|s| s.loadable).count() > 1 { eprintln!( "Monitor ({}) has {} segments, it must only have one", monitor_elf_path.display(), @@ -3097,7 +3364,7 @@ fn main() -> Result<(), String> { let mut bootstrap_invocation_data: Vec = Vec::new(); for invocation in &built_system.bootstrap_invocations { - invocation.add_raw_invocation(&mut bootstrap_invocation_data); + invocation.add_raw_invocation(&kernel_config, &mut bootstrap_invocation_data); } let (_, bootstrap_invocation_data_size) = @@ -3113,7 +3380,7 @@ fn main() -> Result<(), String> { ); let mut stderr = BufWriter::new(std::io::stderr()); for bootstrap_invocation in &built_system.bootstrap_invocations { - bootstrap_invocation.report_fmt(&mut stderr, &built_system.cap_lookup); + bootstrap_invocation.report_fmt(&mut stderr, &kernel_config, &built_system.cap_lookup); } stderr.flush().unwrap(); @@ -3184,7 +3451,12 @@ fn main() -> Result<(), String> { }; let mut report_buf = BufWriter::new(report); - match write_report(&mut report_buf, &built_system, &bootstrap_invocation_data) { + match write_report( + &mut report_buf, + &kernel_config, + &built_system, + &bootstrap_invocation_data, + ) { Ok(()) => report_buf.flush().unwrap(), Err(err) => { return Err(format!( @@ -3206,7 +3478,7 @@ fn main() -> Result<(), String> { } let loader = Loader::new( - kernel_config, + &kernel_config, Path::new(&loader_elf_path), &kernel_elf, &monitor_elf, diff --git a/tool/microkit/src/sdf.rs b/tool/microkit/src/sdf.rs index 9bd2b5409..992b13a14 100644 --- a/tool/microkit/src/sdf.rs +++ b/tool/microkit/src/sdf.rs @@ -4,7 +4,7 @@ // SPDX-License-Identifier: BSD-2-Clause // -use crate::sel4::{Arch, ArmIrqTrigger, Config, PageSize}; +use crate::sel4::{Arch, Config, IrqTrigger, PageSize}; use crate::util::str_to_bool; use crate::MAX_PDS; use std::path::{Path, PathBuf}; @@ -84,7 +84,7 @@ pub struct PlatformDescription { impl PlatformDescription { pub const fn new(kernel_config: &Config) -> PlatformDescription { let page_sizes = match kernel_config.arch { - Arch::Aarch64 => [0x1000, 0x200_000], + Arch::Aarch64 | Arch::Riscv64 => [0x1000, 0x200_000], }; PlatformDescription { page_sizes } @@ -128,7 +128,7 @@ impl SysMemoryRegion { pub struct SysIrq { pub irq: u64, pub id: u64, - pub trigger: ArmIrqTrigger, + pub trigger: IrqTrigger, } // The use of SysSetVar depends on the context. In some @@ -477,8 +477,8 @@ impl ProtectionDomain { let trigger = if let Some(trigger_str) = child.attribute("trigger") { match trigger_str { - "level" => ArmIrqTrigger::Level, - "edge" => ArmIrqTrigger::Edge, + "level" => IrqTrigger::Level, + "edge" => IrqTrigger::Edge, _ => { return Err(value_error( xml_sdf, @@ -489,7 +489,7 @@ impl ProtectionDomain { } } else { // Default the level triggered - ArmIrqTrigger::Level + IrqTrigger::Level }; let irq = SysIrq { diff --git a/tool/microkit/src/sel4.rs b/tool/microkit/src/sel4.rs index e96850a5e..828943a5c 100644 --- a/tool/microkit/src/sel4.rs +++ b/tool/microkit/src/sel4.rs @@ -47,66 +47,113 @@ pub struct Config { pub cap_address_bits: u64, pub fan_out_limit: u64, pub hypervisor: bool, - pub arm_pa_size_bits: usize, pub benchmark: bool, + pub fpu: bool, + /// ARM-specific, number of physical address bits + pub arm_pa_size_bits: Option, + /// RISC-V specific, what kind of virtual memory system (e.g Sv39) + pub riscv_pt_levels: Option, } impl Config { pub fn user_top(&self) -> u64 { match self.arch { Arch::Aarch64 => match self.hypervisor { - true => match self.arm_pa_size_bits { + true => match self.arm_pa_size_bits.unwrap() { 40 => 0x10000000000, 44 => 0x100000000000, _ => panic!("Unknown ARM physical address size bits"), }, false => 0x800000000000, }, + Arch::Riscv64 => 0x0000003ffffff000, } } } pub enum Arch { Aarch64, + Riscv64, +} + +/// RISC-V supports multiple virtual memory systems and so we use this enum +/// to make it easier to support more virtual memory systems in the future. +#[derive(Debug, Copy, Clone)] +pub enum RiscvVirtualMemory { + Sv39, +} + +impl RiscvVirtualMemory { + /// Returns number of page-table levels for a particular virtual memory system. + pub fn levels(self) -> usize { + match self { + RiscvVirtualMemory::Sv39 => 3, + } + } } -#[repr(u64)] #[derive(Debug, Hash, Eq, PartialEq, Copy, Clone)] -#[allow(dead_code)] pub enum ObjectType { - Untyped = 0, - Tcb = 1, - Endpoint = 2, - Notification = 3, - CNode = 4, - SchedContext = 5, - Reply = 6, - HugePage = 7, - VSpace = 8, - SmallPage = 9, - LargePage = 10, - PageTable = 11, - Vcpu = 12, + Untyped, + Tcb, + Endpoint, + Notification, + CNode, + SchedContext, + Reply, + HugePage, + VSpace, + SmallPage, + LargePage, + PageTable, + Vcpu, } impl ObjectType { - pub fn fixed_size(&self) -> Option { + /// Gets the number of bits to represent the size of a object. The + /// size depends on architecture as well as kernel configuration. + pub fn fixed_size_bits(self, config: &Config) -> Option { match self { - ObjectType::Tcb => Some(OBJECT_SIZE_TCB), - ObjectType::Endpoint => Some(OBJECT_SIZE_ENDPOINT), - ObjectType::Notification => Some(OBJECT_SIZE_NOTIFICATION), - ObjectType::Reply => Some(OBJECT_SIZE_REPLY), - ObjectType::VSpace => Some(OBJECT_SIZE_VSPACE), - ObjectType::PageTable => Some(OBJECT_SIZE_PAGE_TABLE), - ObjectType::HugePage => Some(OBJECT_SIZE_HUGE_PAGE), - ObjectType::LargePage => Some(OBJECT_SIZE_LARGE_PAGE), - ObjectType::SmallPage => Some(OBJECT_SIZE_SMALL_PAGE), - ObjectType::Vcpu => Some(OBJECT_SIZE_VCPU), + ObjectType::Tcb => match config.arch { + Arch::Aarch64 => Some(11), + Arch::Riscv64 => match config.fpu { + true => Some(11), + false => Some(10), + }, + }, + ObjectType::Endpoint => Some(4), + ObjectType::Notification => Some(6), + ObjectType::Reply => Some(5), + ObjectType::VSpace => match config.arch { + Arch::Aarch64 => match config.hypervisor { + true => match config.arm_pa_size_bits.unwrap() { + 40 => Some(13), + 44 => Some(12), + _ => { + panic!("Unexpected ARM PA size bits when determining VSpace size bits") + } + }, + false => Some(12), + }, + Arch::Riscv64 => Some(12), + }, + ObjectType::PageTable => Some(12), + ObjectType::HugePage => Some(30), + ObjectType::LargePage => Some(21), + ObjectType::SmallPage => Some(12), + ObjectType::Vcpu => match config.arch { + Arch::Aarch64 => Some(12), + _ => panic!("Unexpected architecture asking for vCPU size bits"), + }, _ => None, } } - pub fn to_str(&self) -> &'static str { + pub fn fixed_size(self, config: &Config) -> Option { + self.fixed_size_bits(config).map(|bits| 1 << bits) + } + + pub fn to_str(self) -> &'static str { match self { ObjectType::Untyped => "SEL4_UNTYPED_OBJECT", ObjectType::Tcb => "SEL4_TCB_OBJECT", @@ -124,15 +171,52 @@ impl ObjectType { } } - pub fn format(&self) -> String { - let object_size = if let Some(fixed_size) = self.fixed_size() { + /// The kernel associates each kernel object with an identifier, which + /// also depends on the configuration of the kernel. + /// When generating the raw invocation to be given to the initial task, + /// this method must be called for any UntypedRetype invocations. + pub fn value(self, config: &Config) -> u64 { + match self { + ObjectType::Untyped => 0, + ObjectType::Tcb => 1, + ObjectType::Endpoint => 2, + ObjectType::Notification => 3, + ObjectType::CNode => 4, + ObjectType::SchedContext => 5, + ObjectType::Reply => 6, + ObjectType::HugePage => 7, + ObjectType::VSpace => match config.arch { + Arch::Aarch64 => 8, + Arch::Riscv64 => 10, + }, + ObjectType::SmallPage => match config.arch { + Arch::Aarch64 => 9, + Arch::Riscv64 => 8, + }, + ObjectType::LargePage => match config.arch { + Arch::Aarch64 => 10, + Arch::Riscv64 => 9, + }, + ObjectType::PageTable => match config.arch { + Arch::Aarch64 => 11, + Arch::Riscv64 => 10, + }, + ObjectType::Vcpu => match config.arch { + Arch::Aarch64 => 12, + _ => panic!("Unknown vCPU object type value for given kernel config"), + }, + } + } + + pub fn format(&self, config: &Config) -> String { + let object_size = if let Some(fixed_size) = self.fixed_size(config) { format!("0x{:x}", fixed_size) } else { "variable size".to_string() }; format!( " object_type {} ({} - {})", - *self as u64, + self.value(config), self.to_str(), object_size ) @@ -156,18 +240,6 @@ impl From for PageSize { } } -pub const OBJECT_SIZE_TCB: u64 = 1 << 11; -pub const OBJECT_SIZE_ENDPOINT: u64 = 1 << 4; -pub const OBJECT_SIZE_NOTIFICATION: u64 = 1 << 6; -pub const OBJECT_SIZE_REPLY: u64 = 1 << 5; -pub const OBJECT_SIZE_PAGE_TABLE: u64 = 1 << 12; -pub const OBJECT_SIZE_HUGE_PAGE: u64 = 1 << 30; -pub const OBJECT_SIZE_LARGE_PAGE: u64 = 1 << 21; -pub const OBJECT_SIZE_SMALL_PAGE: u64 = 1 << 12; -pub const OBJECT_SIZE_VSPACE: u64 = 1 << 13; -pub const OBJECT_SIZE_VCPU: u64 = 1 << 12; -// pub const OBJECT_SIZE_ASID_POOL: u64 = 1 << 12; - /// Virtual memory attributes for ARM /// The values for each enum variant corresponds to what seL4 /// expects when doing a virtual memory invocation. @@ -178,6 +250,14 @@ pub enum ArmVmAttributes { ExecuteNever = 4, } +/// Virtual memory attributes for RISC-V +/// The values for each enum variant corresponds to what seL4 +/// expects when doing a virtual memory invocation. +#[repr(u64)] +pub enum RiscvVmAttributes { + ExecuteNever = 1, +} + impl ArmVmAttributes { #[allow(clippy::should_implement_trait)] // Default::default would return Self, not u64 pub fn default() -> u64 { @@ -185,6 +265,20 @@ impl ArmVmAttributes { } } +impl RiscvVmAttributes { + #[allow(clippy::should_implement_trait)] // Default::default would return Self, not u64 + pub fn default() -> u64 { + 0 + } +} + +pub fn default_vm_attr(config: &Config) -> u64 { + match config.arch { + Arch::Aarch64 => ArmVmAttributes::default(), + Arch::Riscv64 => RiscvVmAttributes::default(), + } +} + #[repr(u32)] #[derive(Copy, Clone)] #[allow(dead_code)] @@ -199,7 +293,8 @@ pub enum Rights { #[repr(u32)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub enum ArmIrqTrigger { +/// The same values apply to all kernel architectures +pub enum IrqTrigger { Level = 0, Edge = 1, } @@ -209,75 +304,263 @@ pub enum ArmIrqTrigger { #[allow(dead_code)] enum InvocationLabel { // Untyped - UntypedRetype = 1, + UntypedRetype, // TCB - TcbReadRegisters = 2, - TcbWriteRegisters = 3, - TcbCopyRegisters = 4, - TcbConfigure = 5, - TcbSetPriority = 6, - TcbSetMCPriority = 7, - TcbSetSchedParams = 8, - TcbSetTimeoutEndpoint = 9, - TcbSetIpcBuffer = 10, - TcbSetSpace = 11, - TcbSuspend = 12, - TcbResume = 13, - TcbBindNotification = 14, - TcbUnbindNotification = 15, - TcbSetTLSBase = 16, + TcbReadRegisters, + TcbWriteRegisters, + TcbCopyRegisters, + TcbConfigure, + TcbSetPriority, + TcbSetMCPriority, + TcbSetSchedParams, + TcbSetTimeoutEndpoint, + TcbSetIpcBuffer, + TcbSetSpace, + TcbSuspend, + TcbResume, + TcbBindNotification, + TcbUnbindNotification, + TcbSetTLSBase, // CNode - CnodeRevoke = 17, - CnodeDelete = 18, - CnodeCancelBadgedSends = 19, - CnodeCopy = 20, - CnodeMint = 21, - CnodeMove = 22, - CnodeMutate = 23, - CnodeRotate = 24, + CnodeRevoke, + CnodeDelete, + CnodeCancelBadgedSends, + CnodeCopy, + CnodeMint, + CnodeMove, + CnodeMutate, + CnodeRotate, // IRQ - IrqIssueIrqHandler = 25, - IrqAckIrq = 26, - IrqSetIrqHandler = 27, - IrqClearIrqHandler = 28, + IrqIssueIrqHandler, + IrqAckIrq, + IrqSetIrqHandler, + IrqClearIrqHandler, // Domain - DomainSetSet = 29, + DomainSetSet, // Scheduling - SchedControlConfigureFlags = 30, - SchedContextBind = 31, - SchedContextUnbind = 32, - SchedContextUnbindObject = 33, - SchedContextConsume = 34, - SchedContextYieldTo = 35, + SchedControlConfigureFlags, + SchedContextBind, + SchedContextUnbind, + SchedContextUnbindObject, + SchedContextConsume, + SchedContextYieldTo, // ARM VSpace - ArmVspaceCleanData = 36, - ArmVspaceInvalidateData = 37, - ArmVspaceCleanInvalidateData = 38, - ArmVspaceUnifyInstruction = 39, + ArmVspaceCleanData, + ArmVspaceInvalidateData, + ArmVspaceCleanInvalidateData, + ArmVspaceUnifyInstruction, // ARM SMC - ArmSmcCall = 40, + ArmSmcCall, // ARM Page table - ArmPageTableMap = 41, - ArmPageTableUnmap = 42, + ArmPageTableMap, + ArmPageTableUnmap, // ARM Page - ArmPageMap = 43, - ArmPageUnmap = 44, - ArmPageCleanData = 45, - ArmPageInvalidateData = 46, - ArmPageCleanInvalidateData = 47, - ArmPageUnifyInstruction = 48, - ArmPageGetAddress = 49, + ArmPageMap, + ArmPageUnmap, + ArmPageCleanData, + ArmPageInvalidateData, + ArmPageCleanInvalidateData, + ArmPageUnifyInstruction, + ArmPageGetAddress, // ARM Asid - ArmAsidControlMakePool = 50, - ArmAsidPoolAssign = 51, + ArmAsidControlMakePool, + ArmAsidPoolAssign, // ARM vCPU - ArmVcpuSetTcb = 52, - ArmVcpuInjectIrq = 53, - ArmVcpuReadReg = 54, - ArmVcpuWriteReg = 55, - ArmVcpuAckVppi = 56, + ArmVcpuSetTcb, + ArmVcpuInjectIrq, + ArmVcpuReadReg, + ArmVcpuWriteReg, + ArmVcpuAckVppi, // ARM IRQ - ArmIrqIssueIrqHandlerTrigger = 57, + ArmIrqIssueIrqHandlerTrigger, + // RISC-V Page Table + RiscvPageTableMap, + RiscvPageTableUnmap, + // RISC-V Page + RiscvPageMap, + RiscvPageUnmap, + RiscvPageGetAddress, + // RISC-V ASID + RiscvAsidControlMakePool, + RiscvAsidPoolAssign, + // RISC-V IRQ + RiscvIrqIssueIrqHandlerTrigger, +} + +impl InvocationLabel { + /// Convert an invocation's named label to the value seL4 expects when + /// you make the invocation. + pub fn to_value(self) -> u32 { + match self { + InvocationLabel::UntypedRetype => 1, + InvocationLabel::TcbReadRegisters => 2, + InvocationLabel::TcbWriteRegisters => 3, + InvocationLabel::TcbCopyRegisters => 4, + InvocationLabel::TcbConfigure => 5, + InvocationLabel::TcbSetPriority => 6, + InvocationLabel::TcbSetMCPriority => 7, + InvocationLabel::TcbSetSchedParams => 8, + InvocationLabel::TcbSetTimeoutEndpoint => 9, + InvocationLabel::TcbSetIpcBuffer => 10, + InvocationLabel::TcbSetSpace => 11, + InvocationLabel::TcbSuspend => 12, + InvocationLabel::TcbResume => 13, + InvocationLabel::TcbBindNotification => 14, + InvocationLabel::TcbUnbindNotification => 15, + InvocationLabel::TcbSetTLSBase => 16, + // CNode + InvocationLabel::CnodeRevoke => 17, + InvocationLabel::CnodeDelete => 18, + InvocationLabel::CnodeCancelBadgedSends => 19, + InvocationLabel::CnodeCopy => 20, + InvocationLabel::CnodeMint => 21, + InvocationLabel::CnodeMove => 22, + InvocationLabel::CnodeMutate => 23, + InvocationLabel::CnodeRotate => 24, + // IRQ + InvocationLabel::IrqIssueIrqHandler => 25, + InvocationLabel::IrqAckIrq => 26, + InvocationLabel::IrqSetIrqHandler => 27, + InvocationLabel::IrqClearIrqHandler => 28, + // Domain + InvocationLabel::DomainSetSet => 29, + // Scheduling + InvocationLabel::SchedControlConfigureFlags => 30, + InvocationLabel::SchedContextBind => 31, + InvocationLabel::SchedContextUnbind => 32, + InvocationLabel::SchedContextUnbindObject => 33, + InvocationLabel::SchedContextConsume => 34, + InvocationLabel::SchedContextYieldTo => 35, + // ARM VSpace + InvocationLabel::ArmVspaceCleanData => 36, + InvocationLabel::ArmVspaceInvalidateData => 37, + InvocationLabel::ArmVspaceCleanInvalidateData => 38, + InvocationLabel::ArmVspaceUnifyInstruction => 39, + // ARM SMC + InvocationLabel::ArmSmcCall => 40, + // ARM Page table + InvocationLabel::ArmPageTableMap => 41, + InvocationLabel::ArmPageTableUnmap => 42, + // ARM Page + InvocationLabel::ArmPageMap => 43, + InvocationLabel::ArmPageUnmap => 44, + InvocationLabel::ArmPageCleanData => 45, + InvocationLabel::ArmPageInvalidateData => 46, + InvocationLabel::ArmPageCleanInvalidateData => 47, + InvocationLabel::ArmPageUnifyInstruction => 48, + InvocationLabel::ArmPageGetAddress => 49, + // ARM ASID + InvocationLabel::ArmAsidControlMakePool => 50, + InvocationLabel::ArmAsidPoolAssign => 51, + // ARM vCPU + InvocationLabel::ArmVcpuSetTcb => 52, + InvocationLabel::ArmVcpuInjectIrq => 53, + InvocationLabel::ArmVcpuReadReg => 54, + InvocationLabel::ArmVcpuWriteReg => 55, + InvocationLabel::ArmVcpuAckVppi => 56, + // ARM IRQ + InvocationLabel::ArmIrqIssueIrqHandlerTrigger => 57, + // RISC-V Page + InvocationLabel::RiscvPageTableMap => 36, + InvocationLabel::RiscvPageTableUnmap => 37, + InvocationLabel::RiscvPageMap => 38, + InvocationLabel::RiscvPageUnmap => 39, + InvocationLabel::RiscvPageGetAddress => 40, + // RISC-V ASID + InvocationLabel::RiscvAsidControlMakePool => 41, + InvocationLabel::RiscvAsidPoolAssign => 42, + // RISC-V IRQ + InvocationLabel::RiscvIrqIssueIrqHandlerTrigger => 43, + } + } +} + +#[derive(Copy, Clone, Default)] +#[allow(dead_code)] +pub struct Riscv64Regs { + pub pc: u64, + pub ra: u64, + pub sp: u64, + pub gp: u64, + pub s0: u64, + pub s1: u64, + pub s2: u64, + pub s3: u64, + pub s4: u64, + pub s5: u64, + pub s6: u64, + pub s7: u64, + pub s8: u64, + pub s9: u64, + pub s10: u64, + pub s11: u64, + pub a0: u64, + pub a1: u64, + pub a2: u64, + pub a3: u64, + pub a4: u64, + pub a5: u64, + pub a6: u64, + pub a7: u64, + pub t0: u64, + pub t1: u64, + pub t2: u64, + pub t3: u64, + pub t4: u64, + pub t5: u64, + pub t6: u64, + pub tp: u64, +} + +impl Riscv64Regs { + pub fn field_names(&self) -> Vec<(&'static str, u64)> { + vec![ + ("pc", self.pc), + ("ra", self.ra), + ("sp", self.sp), + ("gp", self.gp), + ("s0", self.s0), + ("s1", self.s1), + ("s2", self.s2), + ("s3", self.s3), + ("s4", self.s4), + ("s5", self.s5), + ("s6", self.s6), + ("s7", self.s7), + ("s8", self.s8), + ("s9", self.s9), + ("s10", self.s10), + ("s11", self.s11), + ("a0", self.a0), + ("a1", self.a1), + ("a2", self.a2), + ("a3", self.a3), + ("a4", self.a4), + ("a5", self.a5), + ("a6", self.a6), + ("a7", self.a7), + ("t0", self.t0), + ("t1", self.t1), + ("t2", self.t2), + ("t3", self.t3), + ("t4", self.t4), + ("t5", self.t5), + ("t6", self.t6), + ("tp", self.tp), + ] + } + + pub fn as_slice(&self) -> Vec { + vec![ + self.pc, self.ra, self.sp, self.gp, self.s0, self.s1, self.s2, self.s3, self.s4, + self.s5, self.s6, self.s7, self.s8, self.s9, self.s10, self.s11, self.a0, self.a1, + self.a2, self.a3, self.a4, self.a5, self.a6, self.a7, self.t0, self.t1, self.t2, + self.t3, self.t4, self.t5, self.t6, self.tp, + ] + } + + /// Number of registers + pub const LEN: usize = 32; } #[derive(Copy, Clone, Default)] @@ -322,8 +605,8 @@ pub struct Aarch64Regs { } impl Aarch64Regs { - pub fn field_names(&self) -> [(&'static str, u64); Self::LEN] { - [ + pub fn field_names(&self) -> Vec<(&'static str, u64)> { + vec![ ("pc", self.pc), ("sp", self.sp), ("spsr", self.spsr), @@ -363,8 +646,8 @@ impl Aarch64Regs { ] } - pub fn as_slice(&self) -> [u64; Self::LEN] { - [ + pub fn as_slice(&self) -> Vec { + vec![ self.pc, self.sp, self.spsr, @@ -409,15 +692,23 @@ impl Aarch64Regs { } pub struct Invocation { + /// There is some careful context to be aware of when using this field. + /// The 'InvocationLabel' is abstract and does not represent the actual + /// value that seL4 system calls use as it is dependent on the kernel + /// configuration. When we convert this invocation to a list of bytes, we + /// need to use 'label_raw' instead. label: InvocationLabel, + label_raw: u32, args: InvocationArgs, repeat: Option<(u32, InvocationArgs)>, } impl Invocation { - pub fn new(args: InvocationArgs) -> Invocation { + pub fn new(config: &Config, args: InvocationArgs) -> Invocation { + let label = args.to_label(config); Invocation { - label: args.to_label(), + label, + label_raw: label.to_value(), args, repeat: None, } @@ -427,14 +718,15 @@ impl Invocation { /// into raw bytes that will be given to the monitor to interpret /// at runtime. /// Appends to the given data - pub fn add_raw_invocation(&self, data: &mut Vec) { - let (service, args, extra_caps): (u64, Vec, Vec) = self.args.get_args(); + pub fn add_raw_invocation(&self, config: &Config, data: &mut Vec) { + let (service, args, extra_caps): (u64, Vec, Vec) = + self.args.clone().get_args(config); // To potentionally save some allocation, we reserve enough space for all the invocation args data.reserve(2 + args.len() * 8 + extra_caps.len() * 8); let mut tag = Invocation::message_info_new( - self.label as u64, + self.label_raw as u64, 0, extra_caps.len() as u64, args.len() as u64, @@ -452,12 +744,12 @@ impl Invocation { data.extend(arg.to_le_bytes()); } - if let Some((_, repeat)) = self.repeat { + if let Some((_, repeat)) = self.repeat.clone() { // Assert that the variant of the invocation arguments is the // same as the repeat invocation argument variant. assert!(std::mem::discriminant(&self.args) == std::mem::discriminant(&repeat)); - let (repeat_service, repeat_args, repeat_extra_caps) = repeat.get_args(); + let (repeat_service, repeat_args, repeat_extra_caps) = repeat.get_args(config); data.extend(repeat_service.to_le_bytes()); for cap in repeat_extra_caps { data.extend(cap.to_le_bytes()); @@ -524,7 +816,12 @@ impl Invocation { // We do this in an explicit way due to there only being a dozen or so invocations rather // than involving some complicated macros, although maybe there is a better way I am not // aware of. - pub fn report_fmt(&self, f: &mut BufWriter, cap_lookup: &HashMap) { + pub fn report_fmt( + &self, + f: &mut BufWriter, + config: &Config, + cap_lookup: &HashMap, + ) { let mut arg_strs = Vec::new(); let (service, service_str) = match self.args { InvocationArgs::UntypedRetype { @@ -537,7 +834,7 @@ impl Invocation { node_offset, num_objects, } => { - arg_strs.push(object_type.format()); + arg_strs.push(object_type.format(config)); let sz_fmt = if size_bits == 0 { String::from("N/A") } else { @@ -618,16 +915,15 @@ impl Invocation { tcb, resume, arch_flags, - regs, + ref regs, .. } => { arg_strs.push(Invocation::fmt_field_bool("resume", resume)); arg_strs.push(Invocation::fmt_field("arch_flags", arch_flags as u64)); let reg_strs = regs - .field_names() - .into_iter() - .map(|(field, val)| Invocation::fmt_field_reg(field, val)) + .iter() + .map(|(field, val)| Invocation::fmt_field_reg(field, *val)) .collect::>(); arg_strs.push(Invocation::fmt_field_str("regs", reg_strs[0].clone())); for s in ®_strs[1..] { @@ -787,11 +1083,14 @@ impl Invocation { | InvocationLabel::TcbResume | InvocationLabel::TcbWriteRegisters | InvocationLabel::TcbBindNotification => "TCB", - InvocationLabel::ArmAsidPoolAssign => "ASID Pool", - InvocationLabel::ArmIrqIssueIrqHandlerTrigger => "IRQ Control", + InvocationLabel::ArmAsidPoolAssign | InvocationLabel::RiscvAsidPoolAssign => { + "ASID Pool" + } + InvocationLabel::ArmIrqIssueIrqHandlerTrigger + | InvocationLabel::RiscvIrqIssueIrqHandlerTrigger => "IRQ Control", InvocationLabel::IrqSetIrqHandler => "IRQ Handler", - InvocationLabel::ArmPageTableMap => "Page Table", - InvocationLabel::ArmPageMap => "Page", + InvocationLabel::ArmPageTableMap | InvocationLabel::RiscvPageTableMap => "Page Table", + InvocationLabel::ArmPageMap | InvocationLabel::RiscvPageMap => "Page", InvocationLabel::CnodeCopy | InvocationLabel::CnodeMint => "CNode", InvocationLabel::SchedControlConfigureFlags => "SchedControl", InvocationLabel::ArmVcpuSetTcb => "VCPU", @@ -811,10 +1110,14 @@ impl Invocation { InvocationLabel::TcbResume => "Resume", InvocationLabel::TcbWriteRegisters => "WriteRegisters", InvocationLabel::TcbBindNotification => "BindNotification", - InvocationLabel::ArmAsidPoolAssign => "Assign", - InvocationLabel::ArmIrqIssueIrqHandlerTrigger => "Get", + InvocationLabel::ArmAsidPoolAssign | InvocationLabel::RiscvAsidPoolAssign => "Assign", + InvocationLabel::ArmIrqIssueIrqHandlerTrigger + | InvocationLabel::RiscvIrqIssueIrqHandlerTrigger => "Get", InvocationLabel::IrqSetIrqHandler => "SetNotification", - InvocationLabel::ArmPageTableMap | InvocationLabel::ArmPageMap => "Map", + InvocationLabel::ArmPageTableMap + | InvocationLabel::ArmPageMap + | InvocationLabel::RiscvPageTableMap + | InvocationLabel::RiscvPageMap => "Map", InvocationLabel::CnodeCopy => "Copy", InvocationLabel::CnodeMint => "Mint", InvocationLabel::SchedControlConfigureFlags => "ConfigureFlags", @@ -828,7 +1131,7 @@ impl Invocation { } impl InvocationArgs { - fn to_label(self) -> InvocationLabel { + fn to_label(&self, config: &Config) -> InvocationLabel { match self { InvocationArgs::UntypedRetype { .. } => InvocationLabel::UntypedRetype, InvocationArgs::TcbSetSchedParams { .. } => InvocationLabel::TcbSetSchedParams, @@ -837,13 +1140,23 @@ impl InvocationArgs { InvocationArgs::TcbResume { .. } => InvocationLabel::TcbResume, InvocationArgs::TcbWriteRegisters { .. } => InvocationLabel::TcbWriteRegisters, InvocationArgs::TcbBindNotification { .. } => InvocationLabel::TcbBindNotification, - InvocationArgs::AsidPoolAssign { .. } => InvocationLabel::ArmAsidPoolAssign, - InvocationArgs::IrqControlGetTrigger { .. } => { - InvocationLabel::ArmIrqIssueIrqHandlerTrigger - } + InvocationArgs::AsidPoolAssign { .. } => match config.arch { + Arch::Aarch64 => InvocationLabel::ArmAsidPoolAssign, + Arch::Riscv64 => InvocationLabel::RiscvAsidPoolAssign, + }, + InvocationArgs::IrqControlGetTrigger { .. } => match config.arch { + Arch::Aarch64 => InvocationLabel::ArmIrqIssueIrqHandlerTrigger, + Arch::Riscv64 => InvocationLabel::RiscvIrqIssueIrqHandlerTrigger, + }, InvocationArgs::IrqHandlerSetNotification { .. } => InvocationLabel::IrqSetIrqHandler, - InvocationArgs::PageTableMap { .. } => InvocationLabel::ArmPageTableMap, - InvocationArgs::PageMap { .. } => InvocationLabel::ArmPageMap, + InvocationArgs::PageTableMap { .. } => match config.arch { + Arch::Aarch64 => InvocationLabel::ArmPageTableMap, + Arch::Riscv64 => InvocationLabel::RiscvPageTableMap, + }, + InvocationArgs::PageMap { .. } => match config.arch { + Arch::Aarch64 => InvocationLabel::ArmPageMap, + Arch::Riscv64 => InvocationLabel::RiscvPageMap, + }, InvocationArgs::CnodeCopy { .. } => InvocationLabel::CnodeCopy, InvocationArgs::CnodeMint { .. } => InvocationLabel::CnodeMint, InvocationArgs::SchedControlConfigureFlags { .. } => { @@ -853,7 +1166,7 @@ impl InvocationArgs { } } - fn get_args(self) -> (u64, Vec, Vec) { + fn get_args(self, config: &Config) -> (u64, Vec, Vec) { match self { InvocationArgs::UntypedRetype { untyped, @@ -867,7 +1180,7 @@ impl InvocationArgs { } => ( untyped, vec![ - object_type as u64, + object_type.value(config), size_bits, node_index, node_depth, @@ -921,7 +1234,8 @@ impl InvocationArgs { let resume_byte = if resume { 1 } else { 0 }; let flags: u64 = ((arch_flags as u64) << 8) | resume_byte; let mut args = vec![flags, count]; - args.extend(regs.as_slice()); + let regs_values = regs.into_iter().map(|(_, value)| value); + args.extend(regs_values); (tcb, args, vec![]) } InvocationArgs::TcbBindNotification { tcb, notification } => { @@ -1004,7 +1318,7 @@ impl InvocationArgs { } } -#[derive(Clone, Copy)] +#[derive(Clone)] #[allow(dead_code, clippy::large_enum_variant)] pub enum InvocationArgs { UntypedRetype { @@ -1046,7 +1360,7 @@ pub enum InvocationArgs { resume: bool, arch_flags: u8, count: u64, - regs: Aarch64Regs, + regs: Vec<(&'static str, u64)>, }, TcbBindNotification { tcb: u64, @@ -1059,7 +1373,7 @@ pub enum InvocationArgs { IrqControlGetTrigger { irq_control: u64, irq: u64, - trigger: ArmIrqTrigger, + trigger: IrqTrigger, dest_root: u64, dest_index: u64, dest_depth: u64, diff --git a/tool/microkit/src/util.rs b/tool/microkit/src/util.rs index 4258a5d11..a84b0704b 100644 --- a/tool/microkit/src/util.rs +++ b/tool/microkit/src/util.rs @@ -136,6 +136,15 @@ pub fn comma_sep_usize(n: usize) -> String { comma_sep_u64(n as u64) } +pub fn json_str<'a>(json: &'a serde_json::Value, field: &'static str) -> Result<&'a str, String> { + match json.get(field) { + Some(value) => Ok(value + .as_str() + .unwrap_or_else(|| panic!("JSON field '{}' is not a string", field))), + None => Err(format!("JSON field '{}' does not exist", field)), + } +} + pub fn json_str_as_u64(json: &serde_json::Value, field: &'static str) -> Result { match json.get(field) { Some(value) => Ok(value diff --git a/tool/microkit/tests/test.rs b/tool/microkit/tests/test.rs index bc3cf771e..545bd4d1b 100644 --- a/tool/microkit/tests/test.rs +++ b/tool/microkit/tests/test.rs @@ -16,8 +16,10 @@ const DEFAULT_KERNEL_CONFIG: sel4::Config = sel4::Config { cap_address_bits: 64, fan_out_limit: 256, hypervisor: true, - arm_pa_size_bits: 40, benchmark: false, + fpu: true, + arm_pa_size_bits: Some(40), + riscv_pt_levels: None, }; const DEFAULT_PLAT_DESC: sdf::PlatformDescription = From 8a8879a41edb80b031e587348a2005f9b07c1189 Mon Sep 17 00:00:00 2001 From: Ivan Velickovic Date: Wed, 31 Jul 2024 12:19:59 +1000 Subject: [PATCH 03/10] Add support for QEMU virt RISC-V 64-bit Signed-off-by: Ivan Velickovic --- README.md | 1 + build_sdk.py | 14 ++++++ docs/manual.md | 22 +++++++++ example/qemu_virt_riscv64/hello/Makefile | 50 ++++++++++++++++++++ example/qemu_virt_riscv64/hello/hello.c | 16 +++++++ example/qemu_virt_riscv64/hello/hello.system | 11 +++++ 6 files changed, 114 insertions(+) create mode 100644 example/qemu_virt_riscv64/hello/Makefile create mode 100644 example/qemu_virt_riscv64/hello/hello.c create mode 100644 example/qemu_virt_riscv64/hello/hello.system diff --git a/README.md b/README.md index 5179ffc3f..ded00cc90 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,7 @@ The currently supported boards are: * odroidc2 * odroidc4 * qemu_virt_aarch64 +* qemu_virt_riscv64 * tqma8xqp1gb * zcu102 diff --git a/build_sdk.py b/build_sdk.py index 9ff70eacb..8a3fac8fd 100644 --- a/build_sdk.py +++ b/build_sdk.py @@ -201,6 +201,20 @@ class ConfigInfo: "hierarchy": Path("example/qemu_virt_aarch64/hierarchy") } ), + BoardInfo( + name="qemu_virt_riscv64", + arch=KernelArch.RISCV64, + gcc_cpu=None, + loader_link_address=0x80200000, + kernel_options={ + "KernelPlatform": "qemu-riscv-virt", + "KernelIsMCS": True, + "QEMU_MEMORY": "2048", + }, + examples={ + "hello": Path("example/qemu_virt_riscv64/hello"), + } + ), ) SUPPORTED_CONFIGS = ( diff --git a/docs/manual.md b/docs/manual.md index 48511af56..bbf4748a5 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -848,6 +848,28 @@ You can use the following command to simulate a Microkit system: You can find more about the QEMU virt platform in the [QEMU documentation](https://www.qemu.org/docs/master/system/target-arm.html). +## QEMU virt (RISC-V 64-bit) + +Support is available for the virtual RISC-V (64-bit) QEMU platform. +This is a platform that is not based on any specific SoC or hardware platform +and is intended for simulating systems for development or testing. + +It should be noted that the platform support is configured with 2GB of main memory. + +You can use the following command to simulate a Microkit system: + + $ qemu-system-riscv64 \ + -machine virt \ + -nographic \ + -serial mon:stdio \ + -kernel [SYSTEM IMAGE] \ + -m size=2G + +QEMU will start the system image using its packaged version of OpenSBI. + +You can find more about the QEMU virt platform in the +[QEMU documentation](https://www.qemu.org/docs/master/system/target-riscv.html). + ## ZCU102 Initial support is available for the Xilinx ZCU102. diff --git a/example/qemu_virt_riscv64/hello/Makefile b/example/qemu_virt_riscv64/hello/Makefile new file mode 100644 index 000000000..6b67fc73e --- /dev/null +++ b/example/qemu_virt_riscv64/hello/Makefile @@ -0,0 +1,50 @@ +# +# Copyright 2021, Breakaway Consulting Pty. Ltd. +# +# SPDX-License-Identifier: BSD-2-Clause +# +ifeq ($(strip $(BUILD_DIR)),) +$(error BUILD_DIR must be specified) +endif + +ifeq ($(strip $(MICROKIT_SDK)),) +$(error MICROKIT_SDK must be specified) +endif + +ifeq ($(strip $(MICROKIT_BOARD)),) +$(error MICROKIT_BOARD must be specified) +endif + +ifeq ($(strip $(MICROKIT_CONFIG)),) +$(error MICROKIT_CONFIG must be specified) +endif + +TOOLCHAIN := riscv64-unknown-elf + +CC := $(TOOLCHAIN)-gcc +LD := $(TOOLCHAIN)-ld +AS := $(TOOLCHAIN)-as +MICROKIT_TOOL ?= $(MICROKIT_SDK)/bin/microkit + +HELLO_OBJS := hello.o + +BOARD_DIR := $(MICROKIT_SDK)/board/$(MICROKIT_BOARD)/$(MICROKIT_CONFIG) + +IMAGES := hello.elf +CFLAGS := -mstrict-align -nostdlib -ffreestanding -g -O3 -Wall -Wno-unused-function -Werror -I$(BOARD_DIR)/include -march=rv64imafdc_zicsr_zifencei -mabi=lp64d +LDFLAGS := -L$(BOARD_DIR)/lib +LIBS := -lmicrokit -Tmicrokit.ld + +IMAGE_FILE = $(BUILD_DIR)/loader.img +REPORT_FILE = $(BUILD_DIR)/report.txt + +all: $(IMAGE_FILE) + +$(BUILD_DIR)/%.o: %.c Makefile + $(CC) -c $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/hello.elf: $(addprefix $(BUILD_DIR)/, $(HELLO_OBJS)) + $(LD) $(LDFLAGS) $^ $(LIBS) -o $@ + +$(IMAGE_FILE) $(REPORT_FILE): $(addprefix $(BUILD_DIR)/, $(IMAGES)) hello.system + $(MICROKIT_TOOL) hello.system --search-path $(BUILD_DIR) --board $(MICROKIT_BOARD) --config $(MICROKIT_CONFIG) -o $(IMAGE_FILE) -r $(REPORT_FILE) diff --git a/example/qemu_virt_riscv64/hello/hello.c b/example/qemu_virt_riscv64/hello/hello.c new file mode 100644 index 000000000..493c95746 --- /dev/null +++ b/example/qemu_virt_riscv64/hello/hello.c @@ -0,0 +1,16 @@ +/* + * Copyright 2021, Breakaway Consulting Pty. Ltd. + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#include +#include + +void init(void) +{ + microkit_dbg_puts("hello, world\n"); +} + +void notified(microkit_channel ch) +{ +} diff --git a/example/qemu_virt_riscv64/hello/hello.system b/example/qemu_virt_riscv64/hello/hello.system new file mode 100644 index 000000000..146da4bab --- /dev/null +++ b/example/qemu_virt_riscv64/hello/hello.system @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file From 032ebed2c512190c9fb78428f5db673a3010f066 Mon Sep 17 00:00:00 2001 From: Ivan Velickovic Date: Mon, 29 Jul 2024 15:32:52 +1000 Subject: [PATCH 04/10] Add support for Pine64 Star64 Signed-off-by: Ivan Velickovic --- README.md | 1 + build_sdk.py | 13 ++++++++ docs/manual.md | 19 ++++++++++++ example/star64/hello/Makefile | 50 +++++++++++++++++++++++++++++++ example/star64/hello/hello.c | 16 ++++++++++ example/star64/hello/hello.system | 11 +++++++ 6 files changed, 110 insertions(+) create mode 100644 example/star64/hello/Makefile create mode 100644 example/star64/hello/hello.c create mode 100644 example/star64/hello/hello.system diff --git a/README.md b/README.md index ded00cc90..31b096227 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,7 @@ The currently supported boards are: * odroidc4 * qemu_virt_aarch64 * qemu_virt_riscv64 +* star64 * tqma8xqp1gb * zcu102 diff --git a/build_sdk.py b/build_sdk.py index 8a3fac8fd..7315405f8 100644 --- a/build_sdk.py +++ b/build_sdk.py @@ -215,6 +215,19 @@ class ConfigInfo: "hello": Path("example/qemu_virt_riscv64/hello"), } ), + BoardInfo( + name="star64", + arch=KernelArch.RISCV64, + gcc_cpu=None, + loader_link_address=0x60000000, + kernel_options={ + "KernelIsMCS": True, + "KernelPlatform": "star64", + }, + examples={ + "hello": Path("example/star64/hello") + } + ), ) SUPPORTED_CONFIGS = ( diff --git a/docs/manual.md b/docs/manual.md index bbf4748a5..f3078a25b 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -870,6 +870,25 @@ QEMU will start the system image using its packaged version of OpenSBI. You can find more about the QEMU virt platform in the [QEMU documentation](https://www.qemu.org/docs/master/system/target-riscv.html). +## Pine64 Star64 + +Support is available for the Pine64 Star64 platform which is based on the +StarFive JH7110 SoC. + +The platform has a 4GB and 8GB model, we assume the 4GB model. + +The default boot flow of the Star64 is: +1. OpenSBI +2. U-Boot +3. Operating System + +This means that the system image that Microkit produces does not need to be explicitly +packaged with an SBI implementation such as OpenSBI. + +To execute the system image produced by Microkit, execute the following command in U-Boot: + + => go 0x60000000 + ## ZCU102 Initial support is available for the Xilinx ZCU102. diff --git a/example/star64/hello/Makefile b/example/star64/hello/Makefile new file mode 100644 index 000000000..6b67fc73e --- /dev/null +++ b/example/star64/hello/Makefile @@ -0,0 +1,50 @@ +# +# Copyright 2021, Breakaway Consulting Pty. Ltd. +# +# SPDX-License-Identifier: BSD-2-Clause +# +ifeq ($(strip $(BUILD_DIR)),) +$(error BUILD_DIR must be specified) +endif + +ifeq ($(strip $(MICROKIT_SDK)),) +$(error MICROKIT_SDK must be specified) +endif + +ifeq ($(strip $(MICROKIT_BOARD)),) +$(error MICROKIT_BOARD must be specified) +endif + +ifeq ($(strip $(MICROKIT_CONFIG)),) +$(error MICROKIT_CONFIG must be specified) +endif + +TOOLCHAIN := riscv64-unknown-elf + +CC := $(TOOLCHAIN)-gcc +LD := $(TOOLCHAIN)-ld +AS := $(TOOLCHAIN)-as +MICROKIT_TOOL ?= $(MICROKIT_SDK)/bin/microkit + +HELLO_OBJS := hello.o + +BOARD_DIR := $(MICROKIT_SDK)/board/$(MICROKIT_BOARD)/$(MICROKIT_CONFIG) + +IMAGES := hello.elf +CFLAGS := -mstrict-align -nostdlib -ffreestanding -g -O3 -Wall -Wno-unused-function -Werror -I$(BOARD_DIR)/include -march=rv64imafdc_zicsr_zifencei -mabi=lp64d +LDFLAGS := -L$(BOARD_DIR)/lib +LIBS := -lmicrokit -Tmicrokit.ld + +IMAGE_FILE = $(BUILD_DIR)/loader.img +REPORT_FILE = $(BUILD_DIR)/report.txt + +all: $(IMAGE_FILE) + +$(BUILD_DIR)/%.o: %.c Makefile + $(CC) -c $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/hello.elf: $(addprefix $(BUILD_DIR)/, $(HELLO_OBJS)) + $(LD) $(LDFLAGS) $^ $(LIBS) -o $@ + +$(IMAGE_FILE) $(REPORT_FILE): $(addprefix $(BUILD_DIR)/, $(IMAGES)) hello.system + $(MICROKIT_TOOL) hello.system --search-path $(BUILD_DIR) --board $(MICROKIT_BOARD) --config $(MICROKIT_CONFIG) -o $(IMAGE_FILE) -r $(REPORT_FILE) diff --git a/example/star64/hello/hello.c b/example/star64/hello/hello.c new file mode 100644 index 000000000..493c95746 --- /dev/null +++ b/example/star64/hello/hello.c @@ -0,0 +1,16 @@ +/* + * Copyright 2021, Breakaway Consulting Pty. Ltd. + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#include +#include + +void init(void) +{ + microkit_dbg_puts("hello, world\n"); +} + +void notified(microkit_channel ch) +{ +} diff --git a/example/star64/hello/hello.system b/example/star64/hello/hello.system new file mode 100644 index 000000000..146da4bab --- /dev/null +++ b/example/star64/hello/hello.system @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file From 5412de0bd2b83b6997d3faf138ae8cd96e4e6016 Mon Sep 17 00:00:00 2001 From: Ivan Velickovic Date: Fri, 2 Aug 2024 13:04:16 +1000 Subject: [PATCH 05/10] ci: attempt to install RISC-V toolchain Signed-off-by: Ivan Velickovic --- .github/workflows/sdk.yaml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sdk.yaml b/.github/workflows/sdk.yaml index c67463027..ccb9fecb8 100644 --- a/.github/workflows/sdk.yaml +++ b/.github/workflows/sdk.yaml @@ -35,12 +35,13 @@ jobs: sudo apt install software-properties-common sudo add-apt-repository ppa:deadsnakes/ppa sudo apt install \ + gcc-riscv64-unknown-elf \ cmake pandoc device-tree-compiler ninja-build \ texlive-latex-base texlive-latex-recommended \ texlive-fonts-recommended texlive-fonts-extra \ libxml2-utils \ python3.9 python3-pip python3.9-venv \ - qemu-system-arm \ + qemu-system-arm qemu-system-misc \ - name: Install AArch64 GCC toolchain run: | wget -O aarch64-toolchain.tar.gz https://sel4-toolchains.s3.us-east-2.amazonaws.com/arm-gnu-toolchain-12.2.rel1-x86_64-aarch64-none-elf.tar.xz%3Frev%3D28d5199f6db34e5980aae1062e5a6703%26hash%3DF6F5604BC1A2BBAAEAC4F6E98D8DC35B @@ -65,7 +66,10 @@ jobs: ref: microkit path: seL4 - name: Install SDK dependencies - run: brew install pandoc cmake dtc ninja qemu libxml2 python@3.9 coreutils texlive qemu + run: | + brew tap riscv-software-src/riscv + brew install riscv-tools + brew install pandoc cmake dtc ninja qemu libxml2 python@3.9 coreutils texlive qemu - name: Install AArch64 GCC toolchain run: | wget -O aarch64-toolchain.tar.gz https://sel4-toolchains.s3.us-east-2.amazonaws.com/arm-gnu-toolchain-12.2.rel1-darwin-x86_64-aarch64-none-elf.tar.xz%3Frev%3D09b11f159fc24fdda01e05bb32695dd5%26hash%3D6AAF4239F28AE17389AB3E611DFFE0A6 @@ -92,6 +96,8 @@ jobs: - name: Install SDK dependencies run: | rustup target add aarch64-apple-darwin + brew tap riscv-software-src/riscv + brew install riscv-tools brew install pandoc cmake dtc ninja qemu libxml2 python@3.9 coreutils texlive - name: Install AArch64 GCC toolchain run: | From c9ae3112fd83d3f38b29dddd76b86c6012be9d56 Mon Sep 17 00:00:00 2001 From: Ivan Velickovic Date: Fri, 2 Aug 2024 13:09:18 +1000 Subject: [PATCH 06/10] ci: switch to macos-14 Apparently the GCC toolchain with Homebrew only has prebuilt binaries for this version of macOS.... Signed-off-by: Ivan Velickovic --- .github/workflows/sdk.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sdk.yaml b/.github/workflows/sdk.yaml index ccb9fecb8..b3cc8915a 100644 --- a/.github/workflows/sdk.yaml +++ b/.github/workflows/sdk.yaml @@ -55,7 +55,7 @@ jobs: ./pyenv/bin/python build_sdk.py --sel4=seL4 build_macos_x64: name: Build SDK (macOS x86-64) - runs-on: macos-12 + runs-on: macos-14 steps: - name: Checkout Microkit repository uses: actions/checkout@v4 @@ -83,7 +83,7 @@ jobs: ./pyenv/bin/python build_sdk.py --sel4=seL4 build_macos_arm64: name: Build SDK (macOS ARM64) - runs-on: macos-12 + runs-on: macos-14 steps: - name: Checkout Microkit repository uses: actions/checkout@v4 From 67e78788d79de6769187a9f1cd27dafd3d80cf94 Mon Sep 17 00:00:00 2001 From: Ivan Velickovic Date: Sat, 3 Aug 2024 09:52:40 +1000 Subject: [PATCH 07/10] ci: try ubuntu 24.04 GCC version on macOS is 12, on Ubuntu 22.04 it is 10 though and that is causing issues since they do not accept the same -march arguments. Signed-off-by: Ivan Velickovic --- .github/workflows/sdk.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sdk.yaml b/.github/workflows/sdk.yaml index b3cc8915a..b66a606dd 100644 --- a/.github/workflows/sdk.yaml +++ b/.github/workflows/sdk.yaml @@ -18,7 +18,7 @@ on: jobs: build_linux_x64: name: Build SDK (Linux x86-64) - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Checkout Microkit repository uses: actions/checkout@v4 From 47f0ef7dee0047e8a48391d960898c7d2d68720f Mon Sep 17 00:00:00 2001 From: Ivan Velickovic Date: Sat, 3 Aug 2024 10:02:28 +1000 Subject: [PATCH 08/10] Add .orig files to gitignore Signed-off-by: Ivan Velickovic --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 07748d41a..227fa6542 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ pyenv/ release/ tmp_build/ target/ +*.orig From e7c46f2c4642cf02a902ad764d5b2bcbbed98067 Mon Sep 17 00:00:00 2001 From: Ivan-Velickovic Date: Mon, 5 Aug 2024 10:23:40 +1000 Subject: [PATCH 09/10] Add RISC-V dependencies to README Signed-off-by: Ivan-Velickovic --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 31b096227..109909150 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ Please file an issue if additional packages are required. * cmake * ninja-build * ARM GCC compiler for none-elf; version 12.2.1 20221205 +* RISC-V GCC compiler for unknown-elf; version 13.2.0 * device tree compiler * xmllint * qemu-system-aarch64 @@ -64,7 +65,8 @@ On a Debian-like system you can do: pandoc texlive-latex-base texlive-latex-recommended \ texlive-fonts-recommended texlive-fonts-extra \ python3.9 python3.9-venv \ - qemu-system-arm + qemu-system-arm qemu-system-misc \ + gcc-riscv64-unknown-elf If you do not have Python 3.9 available, you can get it via the *deadsnakes* PPA: https://launchpad.net/~deadsnakes/+archive/ubuntu/ppa @@ -77,6 +79,8 @@ To use this: On macOS, with the [Homebrew](https://brew.sh) package manager you can do: $ curl https://sh.rustup.rs -sSf | sh + $ brew tap riscv-software-src/riscv + $ brew install riscv-tools $ brew install pandoc cmake dtc ninja qemu libxml2 python@3.9 coreutils texlive qemu Additonally, a number of Python libraries are needed. From 7f6dabe12760709c0760c05545d53e236b575673 Mon Sep 17 00:00:00 2001 From: Ivan Velickovic Date: Mon, 5 Aug 2024 11:01:31 +1000 Subject: [PATCH 10/10] README: update seL4 version Signed-off-by: Ivan Velickovic --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 109909150..67290819b 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ Please clone seL4 from: The correct branch to use is `microkit`. -Testing has been performed using commit `0cdbffec9cf6b4c7c9c57971cbee5a24a70c8fd0`. +Testing has been performed using commit `4cae30a6ef166a378d4d23697b00106ce7e4e76f`. ## Building the SDK