diff --git a/build_sdk.py b/build_sdk.py index 7e4d62401..e491115aa 100644 --- a/build_sdk.py +++ b/build_sdk.py @@ -72,6 +72,20 @@ class ConfigInfo: examples = { "hello": Path("example/zcu102/hello") } + ), + BoardInfo( + name="imx8mm", + gcc_cpu="cortex-a53", + loader_link_address=0x41000000, + kernel_options = { + "KernelPlatform": "imx8mm-evk", + "KernelIsMCS": True, + "KernelArmExportPCNTUser": True, + "KernelArmExportPMUUser": True, + }, + examples = { + "ethernet": Path("example/imx8mm/passive_server") + } ) ) @@ -81,6 +95,14 @@ class ConfigInfo: debug=False, kernel_options = {}, ), + ConfigInfo( + name="benchmark", + debug=False, + kernel_options = { + "KernelDebugBuild": False, + "KernelBenchmarks": "track_utilisation" + }, + ), ConfigInfo( name="debug", debug=True, @@ -89,7 +111,7 @@ class ConfigInfo: "KernelPrinting": True, "KernelVerificationBuild": False } - ), + ) ) diff --git a/example/imx8mm/passive_server/Makefile b/example/imx8mm/passive_server/Makefile new file mode 100644 index 000000000..49f078f23 --- /dev/null +++ b/example/imx8mm/passive_server/Makefile @@ -0,0 +1,59 @@ +# +# 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 $(SEL4CP_SDK)),) +$(error SEL4CP_SDK must be specified) +endif + +ifeq ($(strip $(SEL4CP_BOARD)),) +$(error SEL4CP_BOARD must be specified) +endif + +ifeq ($(strip $(SEL4CP_CONFIG)),) +$(error SEL4CP_CONFIG must be specified) +endif + +TOOLCHAIN := aarch64-unknown-linux-gnu + +CPU := cortex-a53 + +CC := $(TOOLCHAIN)-gcc +LD := $(TOOLCHAIN)-ld +AS := $(TOOLCHAIN)-as +SEL4CP_TOOL ?= $(SEL4CP_SDK)/bin/sel4cp + +SERVER_OBJS := server.o +CLIENT_OBJS := client.o + +BOARD_DIR := $(SEL4CP_SDK)/board/$(SEL4CP_BOARD)/$(SEL4CP_CONFIG) + +IMAGES := server.elf client.elf +CFLAGS := -mcpu=$(CPU) -mstrict-align -nostdlib -ffreestanding -g3 -O3 -Wall -Wno-unused-function -Werror -I$(BOARD_DIR)/include +LDFLAGS := -L$(BOARD_DIR)/lib +LIBS := -lsel4cp -Tsel4cp.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)/%.o: %.s Makefile + $(AS) -g3 -mcpu=$(CPU) $< -o $@ + +$(BUILD_DIR)/server.elf: $(addprefix $(BUILD_DIR)/, $(SERVER_OBJS)) + $(LD) $(LDFLAGS) $^ $(LIBS) -o $@ + +$(BUILD_DIR)/client.elf: $(addprefix $(BUILD_DIR)/, $(CLIENT_OBJS)) + $(LD) $(LDFLAGS) $^ $(LIBS) -o $@ + +$(IMAGE_FILE) $(REPORT_FILE): $(addprefix $(BUILD_DIR)/, $(IMAGES)) passive_server.system + $(SEL4CP_TOOL) passive_server.system --search-path $(BUILD_DIR) --board $(SEL4CP_BOARD) --config $(SEL4CP_CONFIG) -o $(IMAGE_FILE) -r $(REPORT_FILE) diff --git a/example/imx8mm/passive_server/client.c b/example/imx8mm/passive_server/client.c new file mode 100644 index 000000000..a1a9e2691 --- /dev/null +++ b/example/imx8mm/passive_server/client.c @@ -0,0 +1,24 @@ +/* + * Copyright 2022, UNSW + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#include + +#define SERVER_CH 0 + +void +init(void) +{ + sel4cp_dbg_puts("client: client protection domain init function running\n"); + + /* message the server */ + sel4cp_mr_set(0, 0); + (void) sel4cp_ppcall(SERVER_CH, sel4cp_msginfo_new(1, 1)); +} + +void +notified(sel4cp_channel ch) +{ + sel4cp_dbg_puts("Client recieved a notification on an unexpected channel\n"); +} diff --git a/example/imx8mm/passive_server/passive_server.system b/example/imx8mm/passive_server/passive_server.system new file mode 100644 index 000000000..6f70d6d85 --- /dev/null +++ b/example/imx8mm/passive_server/passive_server.system @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/example/imx8mm/passive_server/server.c b/example/imx8mm/passive_server/server.c new file mode 100644 index 000000000..cae961850 --- /dev/null +++ b/example/imx8mm/passive_server/server.c @@ -0,0 +1,33 @@ +/* + * Copyright 2022, UNSW + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#include + +sel4cp_msginfo +protected(sel4cp_channel ch, sel4cp_msginfo msginfo) +{ + switch (sel4cp_msginfo_get_label(msginfo)) { + case 0: + sel4cp_dbg_puts("Server is running on clients scheduling context\n"); + break; + default: + sel4cp_dbg_puts("server received an unexpected message\n"); + } + + return seL4_MessageInfo_new(0, 0, 0, 0); +} + +void +init(void) +{ + sel4cp_dbg_puts("server: server protection domain init function running\n"); + /* Nothing to initialise */ +} + +void +notified(sel4cp_channel ch) +{ + sel4cp_dbg_puts("Server recieved a notification on an unexpected channel\n"); +} diff --git a/libsel4cp/include/sel4cp.h b/libsel4cp/include/sel4cp.h index 395a37995..9ba7c9e76 100644 --- a/libsel4cp/include/sel4cp.h +++ b/libsel4cp/include/sel4cp.h @@ -8,6 +8,7 @@ #pragma once #include +#include #define __thread #include @@ -15,6 +16,8 @@ typedef unsigned int sel4cp_channel; typedef seL4_MessageInfo_t sel4cp_msginfo; +#define MONITOR_EP 5 +#define TCB_CAP 6 #define BASE_OUTPUT_NOTIFICATION_CAP 10 #define BASE_ENDPOINT_CAP 74 #define BASE_IRQ_CAP 138 @@ -27,6 +30,10 @@ void notified(sel4cp_channel ch); sel4cp_msginfo protected(sel4cp_channel ch, sel4cp_msginfo msginfo); extern char sel4cp_name[16]; +/* These next three variables are so our PDs can combine a signal with the next Recv syscall */ +extern bool have_signal; +extern seL4_CPtr signal; +extern seL4_MessageInfo_t signal_msg; /* * Output a single character on the debug console. diff --git a/libsel4cp/src/main.c b/libsel4cp/src/main.c index c305467c4..16d652c21 100644 --- a/libsel4cp/src/main.c +++ b/libsel4cp/src/main.c @@ -19,7 +19,11 @@ char _stack[4096] __attribute__((__aligned__(16))); +bool passive; char sel4cp_name[16]; +bool have_signal = false; +seL4_CPtr signal; +seL4_MessageInfo_t signal_msg; extern seL4_IPCBuffer __sel4_ipc_buffer_obj; @@ -54,6 +58,8 @@ handler_loop(void) if (have_reply) { tag = seL4_ReplyRecv(INPUT_CAP, reply_tag, &badge, REPLY_CAP); + } else if (have_signal) { + tag = seL4_NBSendRecv(signal, signal_msg, INPUT_CAP, &badge, REPLY_CAP); } else { tag = seL4_Recv(INPUT_CAP, &badge, REPLY_CAP); } @@ -82,5 +88,19 @@ main(void) { run_init_funcs(); init(); + + /* + If we are passive, now our initialisation is complete we can + signal the monitor to unbind our scheduling context and bind + it to our notification object. + We delay this signal so we are ready waiting on a recv() syscall + */ + if (passive) { + have_signal = true; + signal_msg = seL4_MessageInfo_new(0, 0, 0, 1); + seL4_SetMR(0, 0); + signal = (MONITOR_EP); + } + handler_loop(); } diff --git a/loader/src/loader.c b/loader/src/loader.c index ffd2bb277..09840993d 100644 --- a/loader/src/loader.c +++ b/loader/src/loader.c @@ -26,10 +26,6 @@ _Static_assert(sizeof(uintptr_t) == 8 || sizeof(uintptr_t) == 4, "Expect uintptr #define STACK_SIZE 4096 -#define UART_BASE 0x5a070000 -#define STAT 0x14 -#define TRANSMIT 0x1c -#define STAT_TDRE (1 << 23) #define UART_REG(x) ((volatile uint32_t *)(UART_BASE + (x))) #if defined(BOARD_zcu102) @@ -113,6 +109,23 @@ memcpy(void *dst, const void *src, size_t sz) } #if defined(BOARD_tqma8xqp1gb) +#define UART_BASE 0x5a070000 +#define STAT 0x14 +#define TRANSMIT 0x1c +#define STAT_TDRE (1 << 23) + +static void +putc(uint8_t ch) +{ + while (!(*UART_REG(STAT) & STAT_TDRE)) { } + *UART_REG(TRANSMIT) = ch; +} +#elif defined(BOARD_imx8mm) +#define UART_BASE 0x30890000 +#define STAT 0x98 +#define TRANSMIT 0x40 +#define STAT_TDRE (1 << 14) + static void putc(uint8_t ch) { @@ -120,6 +133,11 @@ putc(uint8_t ch) *UART_REG(TRANSMIT) = ch; } #elif defined(BOARD_zcu102) +#define UART_BASE 0x5a070000 +#define STAT 0x14 +#define TRANSMIT 0x1c +#define STAT_TDRE (1 << 23) + static void putc(uint8_t ch) { diff --git a/monitor/src/main.c b/monitor/src/main.c index 176891366..4f2067d75 100644 --- a/monitor/src/main.c +++ b/monitor/src/main.c @@ -79,7 +79,7 @@ * * FIXME: This can be smaller once compression is enabled. */ -#define BOOTSTRAP_INVOCATION_DATA_SIZE 90 +#define BOOTSTRAP_INVOCATION_DATA_SIZE 110 seL4_IPCBuffer *__sel4_ipc_buffer; @@ -90,6 +90,8 @@ static char pd_names[MAX_PDS][MAX_NAME_LEN]; seL4_Word fault_ep; seL4_Word reply; seL4_Word tcbs[MAX_TCBS]; +seL4_Word scheduling_contexts[MAX_TCBS]; +seL4_Word notification_caps[MAX_TCBS]; struct region { uintptr_t paddr; @@ -369,6 +371,19 @@ monitor(void) puthex64(tcb_cap); puts("\n"); + if (label == seL4_Fault_NullFault && badge < MAX_PDS) { + /* This is a request from our PD to become passive */ + err = seL4_SchedContext_UnbindObject(scheduling_contexts[badge], tcb_cap); + err = seL4_SchedContext_Bind(scheduling_contexts[badge], notification_caps[badge]); + if (err != seL4_NoError) { + puts("error binding scheduling context to notification"); + } else { + puts(pd_names[badge]); + puts(" is now passive!\n"); + } + continue; + } + if (badge < MAX_PDS && pd_names[badge][0] != 0) { puts("faulting PD: "); puts(pd_names[badge]); diff --git a/tool/sel4coreplat/__main__.py b/tool/sel4coreplat/__main__.py index 1a299646f..959e782c2 100644 --- a/tool/sel4coreplat/__main__.py +++ b/tool/sel4coreplat/__main__.py @@ -64,6 +64,7 @@ Sel4TcbBindNotification, Sel4TcbResume, Sel4CnodeMint, + Sel4CnodeCopy, Sel4UntypedRetype, Sel4IrqControlGet, Sel4IrqHandlerSetNotification, @@ -154,6 +155,8 @@ def max_untyped_objects(self, symbol_size: int) -> int: FAULT_EP_CAP_IDX = 2 VSPACE_CAP_IDX = 3 REPLY_CAP_IDX = 4 +MONITOR_EP_CAP_IDX = 5 +TCB_CAP_IDX = 6 BASE_OUTPUT_NOTIFICATION_CAP = 10 BASE_OUTPUT_ENDPOINT_CAP = BASE_OUTPUT_NOTIFICATION_CAP + 64 BASE_IRQ_CAP = BASE_OUTPUT_ENDPOINT_CAP + 64 @@ -600,6 +603,8 @@ class BuiltSystem: reply_cap_address: int cap_lookup: Dict[int, str] tcb_caps: List[int] + sched_caps: List[int] + ntfn_caps: List[int] regions: List[Region] kernel_objects: List[KernelObject] initial_task_virt_region: MemoryRegion @@ -1079,6 +1084,7 @@ def build_system( tcb_caps = [tcb_obj.cap_addr for tcb_obj in tcb_objects] schedcontext_names = [f"SchedContext: PD={pd.name}" for pd in system.protection_domains] schedcontext_objects = init_system.allocate_objects(SEL4_SCHEDCONTEXT_OBJECT, schedcontext_names, size=PD_SCHEDCONTEXT_SIZE) + schedcontext_caps = [sc.cap_addr for sc in schedcontext_objects] pp_protection_domains = [pd for pd in system.protection_domains if pd.pp] endpoint_names = ["EP: Monitor Fault"] + [f"EP: PD={pd.name}" for pd in pp_protection_domains] reply_names = ["Reply: Monitor"]+ [f"Reply: PD={pd.name}" for pd in system.protection_domains] @@ -1092,6 +1098,7 @@ def build_system( notification_names = [f"Notification: PD={pd.name}" for pd in system.protection_domains] notification_objects = init_system.allocate_objects(SEL4_NOTIFICATION_OBJECT, notification_names) notification_objects_by_pd = dict(zip(system.protection_domains, notification_objects)) + notification_caps = [ntfn.cap_addr for ntfn in notification_objects] # Determine number of upper directory / directory / page table objects required # @@ -1376,6 +1383,18 @@ def build_system( pd_b_badge) ) + # mint a cap between monitor and passive PDs. + for idx, (cnode_obj, pd) in enumerate(zip(cnode_objects, system.protection_domains), 1): + if pd.passive: + system_invocations.append(Sel4CnodeMint( + cnode_obj.cap_addr, + MONITOR_EP_CAP_IDX, + PD_CAP_BITS, + root_cnode_cap, + fault_ep_endpoint_object.cap_addr, + kernel_config.cap_address_bits, + SEL4_RIGHTS_ALL, + idx)) # All minting is complete at this point @@ -1443,6 +1462,10 @@ def build_system( for tcb_obj, schedcontext_obj, pd in zip(tcb_objects, schedcontext_objects, system.protection_domains): system_invocations.append(Sel4TcbSetSchedParams(tcb_obj.cap_addr, INIT_TCB_CAP_ADDRESS, pd.priority, pd.priority, schedcontext_obj.cap_addr, fault_ep_endpoint_object.cap_addr)) + # Copy the PD's TCB cap into their address space for development purposes. + for tcb_obj, cnode_obj in zip(tcb_objects, cnode_objects): + system_invocations.append(Sel4CnodeCopy(cnode_obj.cap_addr, TCB_CAP_IDX, PD_CAP_BITS, root_cnode_cap, tcb_obj.cap_addr, kernel_config.cap_address_bits, SEL4_RIGHTS_ALL)) + # set vspace / cspace (SetSpace) invocation = Sel4TcbSetSpace(tcb_objects[0].cap_addr, badged_fault_ep, cnode_objects[0].cap_addr, kernel_config.cap_address_bits - PD_CAP_BITS, vspace_objects[0].cap_addr, 0) invocation.repeat(len(system.protection_domains), tcb=1, fault_ep=1, cspace_root=1, vspace_root=1) @@ -1486,6 +1509,7 @@ def build_system( for pd in system.protection_domains: # Could use pd.elf_file.write_symbol here to update variables if required. pd_elf_files[pd].write_symbol("sel4cp_name", pack("<16s", pd.name.encode("utf8"))) + pd_elf_files[pd].write_symbol("passive", pack("?", pd.passive)) for pd in system.protection_domains: for setvar in pd.setvars: @@ -1514,6 +1538,8 @@ def build_system( reply_cap_address = reply_object.cap_addr, cap_lookup = cap_address_names, tcb_caps = tcb_caps, + sched_caps = schedcontext_caps, + ntfn_caps = notification_caps, regions = regions, kernel_objects = init_system._objects, initial_task_phys_region = initial_task_phys_region, @@ -1682,9 +1708,13 @@ def main() -> int: regions += [(r.addr, r.data) for r in built_system.regions] tcb_caps = built_system.tcb_caps + sched_caps = built_system.sched_caps + ntfn_caps = built_system.ntfn_caps monitor_elf.write_symbol("fault_ep", pack(" SysMemoryRegio def xml2pd(pd_xml: ET.Element) -> ProtectionDomain: - _check_attrs(pd_xml, ("name", "priority", "pp", "budget", "period")) + _check_attrs(pd_xml, ("name", "priority", "pp", "budget", "period", "passive")) program_image: Optional[Path] = None name = checked_lookup(pd_xml, "name") priority = int(pd_xml.attrib.get("priority", "0"), base=0) @@ -237,6 +238,7 @@ def xml2pd(pd_xml: ET.Element) -> ProtectionDomain: raise ValueError(f"budget ({budget}) must be less than, or equal to, period ({period})") pp = str_to_bool(pd_xml.attrib.get("pp", "false")) + passive = str_to_bool(pd_xml.attrib.get("passive", "false")) maps = [] irqs = [] @@ -277,7 +279,7 @@ def xml2pd(pd_xml: ET.Element) -> ProtectionDomain: if program_image is None: raise ValueError("program_image must be specified") - return ProtectionDomain(name, priority, budget, period, pp, program_image, tuple(maps), tuple(irqs), tuple(setvars), pd_xml) + return ProtectionDomain(name, priority, budget, period, pp, passive, program_image, tuple(maps), tuple(irqs), tuple(setvars), pd_xml) def xml2channel(ch_xml: ET.Element) -> Channel: