blob: b0a1a4da8197207ac76675ce8e5382fe59ce7af6 [file] [log] [blame]
/*
* 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();
}