|  | /* | 
|  | * 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/base/csr.h" | 
|  | #include "sw/device/lib/dif/dif_rv_timer.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" | 
|  |  | 
|  | 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))); | 
|  |  | 
|  | 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 dif_rv_timer_t rv_timer; | 
|  | static uint32_t supervisor_custom_fn_addr = 0; | 
|  |  | 
|  | // Overrides the default supervisor ecall handler. | 
|  | // In this test, any ecalls indicate that we took an unexpected interrupt, | 
|  | // so fail the test. | 
|  | void ottf_supervisor_ecall_handler(void) { test_status_set(kTestStatusFailed); } | 
|  |  | 
|  | // Function that will execute in S-mode. | 
|  | // Triggers a test pass. | 
|  | __attribute__((section(".super_virtual"))) void smode_fn(void) { | 
|  | test_status_set(kTestStatusPassed); | 
|  | } | 
|  |  | 
|  | // S-mode trap handler. | 
|  | // All entries are ecall, except the timer entry. | 
|  | static __attribute__((section(".super_virtual"))) | 
|  | __attribute__((naked, aligned(256))) void | 
|  | stvec(void) { | 
|  | asm volatile( | 
|  | ".option push \n" | 
|  | ".option norvc \n" | 
|  | "ecall \n" | 
|  | "ecall \n" | 
|  | "ecall \n" | 
|  | "ecall \n" | 
|  | "ecall \n" | 
|  | // Timer interrupt is index 5 | 
|  | "j smode_fn \n" | 
|  | "ecall \n" | 
|  | "ecall \n" | 
|  | "ecall \n" | 
|  | "ecall \n" | 
|  | "ecall \n" | 
|  | "ecall \n" | 
|  | "ecall \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); | 
|  | } | 
|  |  | 
|  | dif_rv_timer_tick_params_t tick_params; | 
|  | CHECK_DIF_OK(dif_rv_timer_approximate_tick_params(kClockFreqPeripheralHz, | 
|  | 1000000, &tick_params)); | 
|  | CHECK_DIF_OK(dif_rv_timer_init( | 
|  | mmio_region_from_addr(TOP_MATCHA_RV_TIMER_SMC_BASE_ADDR), &rv_timer)); | 
|  | CHECK_DIF_OK(dif_rv_timer_set_tick_params(&rv_timer, 0, tick_params)); | 
|  | CHECK_DIF_OK(dif_rv_timer_irq_set_enabled( | 
|  | &rv_timer, kDifRvTimerIrqTimerExpiredHart0Timer0, kDifToggleEnabled)); | 
|  | uint64_t counter_start; | 
|  | CHECK_DIF_OK(dif_rv_timer_counter_read(&rv_timer, 0, &counter_start)); | 
|  | CHECK_DIF_OK(dif_rv_timer_arm(&rv_timer, 0, 0, counter_start + 1000)); | 
|  | CHECK_DIF_OK( | 
|  | dif_rv_timer_counter_set_enabled(&rv_timer, 0, kDifToggleEnabled)); | 
|  |  | 
|  | // 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 for supervisor-mode | 
|  | 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 supervisor's | 
|  | // virtual memory space. | 
|  | 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, false); | 
|  |  | 
|  | uint32_t unused_supervisor_paddr = | 
|  | (uint32_t)&__SUPER_VIRTUAL_ROM + super_virtual_size_padded; | 
|  | // This code enables the timer interrupt, followed by an ecall. | 
|  | // When placed near a page boundary with the timer already expired, | 
|  | // this should cause an interrupt during a page table walk. | 
|  | uint32_t enable_isr_code[] = { | 
|  | 0x02000513,  // li a0,32 | 
|  | 0x10452073,  // csrs sie,a0 | 
|  | 0x00000073,  // ecall | 
|  | }; | 
|  | // Offset the code such that it straddles the page boundary. | 
|  | uint32_t offset = PAGE_SIZE - (sizeof(uint32_t) * 2); | 
|  | uint32_t target_paddr = unused_supervisor_paddr + offset; | 
|  | memcpy((uint32_t *)target_paddr, enable_isr_code, sizeof(enable_isr_code)); | 
|  | supervisor_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 the timer interrupt to supervisor mode. | 
|  | uint32_t mideleg = (1 << 5); | 
|  |  | 
|  | // Wait until the timer interrupt 'expires'. That way, we will receive | 
|  | // it immediately upon enabling supervisor interrupts. | 
|  | dif_rv_timer_irq_state_snapshot_t snapshot = 0; | 
|  | do { | 
|  | CHECK_DIF_OK(dif_rv_timer_irq_get_state(&rv_timer, 0, &snapshot)); | 
|  | } while (!snapshot); | 
|  |  | 
|  | // Execute supervisor_custom_fn_addr_fn in supervisor mode, after enabling | 
|  | // virtual memory. | 
|  | supervisor_trampoline((uint32_t)supervisor_custom_fn_addr, (uint32_t)satp, 0, | 
|  | (uint32_t)stvec | 1, (uint32_t)mideleg); | 
|  | __builtin_unreachable(); | 
|  | } |