blob: 00eba948c1feb634da23d725e04853f4e6b3a892 [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/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;
// Execute a load and store immediately before the page boundary.
// Ecall afterwards to cause test success, if the memory ops
// were okay.
uint32_t load_store_code[] = {
0xff8025b7, // lui a1,0xff800
0xc1944194, // lw a3, 0(a1); sw a3,0(a1)
0x00000073, // ecall
};
uint32_t offset = PAGE_SIZE - (sizeof(uint32_t) * 2);
uint32_t target_paddr = unused_user_paddr + offset;
memcpy((uint32_t *)target_paddr, load_store_code, sizeof(load_store_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);
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();
}