| /* |
| * Copyright 2023 Google LLC |
| * Copyright lowRISC contributors |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| |
| #include "hw/top_matcha/sw/autogen/top_matcha.h" |
| #include "sw/device/lib/runtime/log.h" |
| #include "sw/device/lib/runtime/print.h" |
| #include "sw/device/lib/testing/test_framework/check.h" |
| #include "sw/device/lib/testing/test_framework/ottf_test_config.h" |
| #include "sw/device/lib/testing/test_framework/status.h" |
| #include "sw/device/lib/testing/test_framework/test_util.h" |
| #include "sw/device/lib/virtual_memory.h" |
| |
| #define CAUSE_USER_ECALL (0x8) |
| uint32_t supervisor_l1pt[PAGE_SIZE / sizeof(uint32_t)] |
| __attribute__((aligned(PAGE_SIZE))); |
| uint32_t supervisor_l2pt[PAGE_SIZE / sizeof(uint32_t)] |
| __attribute__((aligned(PAGE_SIZE))); |
| uint32_t user_l2pt[PAGE_SIZE / sizeof(uint32_t)] |
| __attribute__((aligned(PAGE_SIZE))); |
| |
| extern uint32_t __VIRTUAL_ROM; |
| extern uint32_t __virtual_start; |
| extern uint32_t __virtual_end; |
| extern uint32_t __SUPER_VIRTUAL_ROM; |
| extern uint32_t __super_virtual_start; |
| extern uint32_t __super_virtual_end; |
| |
| OTTF_DEFINE_TEST_CONFIG(); |
| |
| static dif_uart_t smc_uart; |
| static uint32_t umode_custom_fn_addr = 0; |
| |
| // Overrides the default supervisor ecall handler. |
| // From here, we will set the test pass. |
| void ottf_supervisor_ecall_handler(void) { test_status_set(kTestStatusPassed); } |
| |
| // Function that will execute in S-mode. |
| // Passes a virtual function address to the U-mode trampoline. |
| static __attribute__((section(".super_virtual"))) void smode_fn(void) { |
| user_trampoline((uint32_t)umode_custom_fn_addr); |
| } |
| |
| // S-mode trap handler. |
| // We simply ecall into M-mode. |
| static __attribute__((section(".super_virtual"))) |
| __attribute__((naked, aligned(256))) void |
| stvec(void) { |
| asm volatile( |
| ".option push \n" |
| ".option norvc \n" |
| "ecall \n" |
| ".option pop \n"); |
| } |
| |
| void _ottf_main(void) { |
| test_status_set(kTestStatusInTest); |
| |
| // Initialize the SMC UART to enable logging for non-DV simulation platforms. |
| if (kDeviceType != kDeviceSimDV) { |
| init_uart(TOP_MATCHA_SMC_UART_BASE_ADDR, &smc_uart); |
| } |
| |
| // Map our code space and UART peripheral. |
| // Both are mapped such that the virtual addresses |
| // and physical addresses are the same, but this still |
| // causes address translation to happen. |
| machine_map_megapage((void *)TOP_MATCHA_RAM_SMC_BASE_ADDR, |
| (void *)TOP_MATCHA_RAM_SMC_BASE_ADDR, |
| (uint32_t *)supervisor_l1pt); |
| machine_map_megapage((void *)TOP_MATCHA_SMC_UART_BASE_ADDR, |
| (void *)TOP_MATCHA_SMC_UART_BASE_ADDR, |
| (uint32_t *)supervisor_l1pt); |
| |
| // Set up 2-level page tables, one set for user-mode and one set for |
| // supervisor-mode. |
| machine_map_region((uint32_t)&__VIRTUAL_ROM, (uint32_t)&__virtual_start, |
| (uint32_t)&__virtual_end, supervisor_l1pt, user_l2pt, |
| true); |
| machine_map_region( |
| (uint32_t)&__SUPER_VIRTUAL_ROM, (uint32_t)&__super_virtual_start, |
| (uint32_t)&__super_virtual_end, supervisor_l1pt, supervisor_l2pt, false); |
| |
| // Map the next two physical pages after supervisor's space into user-mode's |
| // virtual space. We'll use the boundary between the two new pages to trigger |
| // page table walks. NB: We add the new pages to the supervisor_l2pt since |
| // they reside in the same 4MB segment. |
| uint32_t super_virtual_size = &__super_virtual_end - &__super_virtual_start; |
| uint32_t super_virtual_size_padded = |
| (super_virtual_size + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); |
| machine_map_region( |
| (uint32_t)&__SUPER_VIRTUAL_ROM + super_virtual_size_padded, |
| (uint32_t)&__super_virtual_start + super_virtual_size_padded, |
| (uint32_t)&__super_virtual_start + super_virtual_size_padded + |
| (PAGE_SIZE * 2), |
| supervisor_l1pt, supervisor_l2pt, true); |
| |
| uint32_t unused_user_paddr = |
| (uint32_t)&__SUPER_VIRTUAL_ROM + super_virtual_size_padded; |
| uint32_t nop_code[] = { |
| 0x00000013, // nop |
| 0x00000013, // nop |
| 0x00000013, // nop |
| 0x00000013, // nop |
| 0x00000013, // nop |
| 0x00000013, // nop |
| 0x00000013, // nop |
| 0x00000013, // nop |
| 0x00000073, // ecall |
| }; |
| // Generate a target address that puts ~half of the code on each |
| // side of the page boundary. |
| uint32_t offset = PAGE_SIZE - (sizeof(nop_code) / 2); |
| uint32_t target_paddr = unused_user_paddr + offset; |
| memcpy((uint32_t *)target_paddr, nop_code, sizeof(nop_code)); |
| umode_custom_fn_addr = |
| (uint32_t)&__super_virtual_start + super_virtual_size_padded + offset; |
| |
| // Calculate the value of the SATP register. |
| // We enable SV32 virtual memory, and store the page |
| // containing our page tables. |
| uint32_t satp = (uint32_t)(0x1llu << 31) | ((uint32_t)supervisor_l1pt >> 12); |
| |
| // Delegate U-mode ecalls to S-mode. |
| uint32_t medeleg = (1 << CAUSE_USER_ECALL); |
| |
| // Execute smode_fn in supervisor mode, after enabling virtual memory. |
| supervisor_trampoline((uint32_t)smode_fn, (uint32_t)satp, (uint32_t)medeleg, |
| (uint32_t)stvec, 0); |
| __builtin_unreachable(); |
| } |