blob: 98bb7e95db910409bcb9bcdab1441648f52d7026 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include "sw/device/lib/base/csr.h"
#include "sw/device/lib/handler.h"
#include "sw/device/lib/irq.h"
#include "sw/device/lib/runtime/hart.h"
#include "sw/device/lib/runtime/pmp.h"
#include "sw/device/lib/testing/check.h"
#include "sw/device/lib/testing/test_main.h"
#include "sw/device/lib/testing/test_status.h"
#define PMP_LOAD_REGION_ID 0
#define PMP_LOAD_RANGE_BUFFER_SIZE 2048
#define PMP_LOAD_RANGE_SIZE 1024
#define PMP_LOAD_RANGE_BOTTOM_OFFSET 0
#define PMP_LOAD_RANGE_TOP_OFFSET 1023
// These flags are used in the test routine to verify that a corresponding
// interrupt has elapsed, and has been serviced. These are declared as volatile
// since they are referenced in the ISR routine as well as in the main program
// flow.
static volatile bool pmp_load_exception;
/**
* The buffer that is used for load/store access violation test.
*/
__attribute__((aligned(PMP_LOAD_RANGE_SIZE))) //
static volatile char pmp_load_store_test_data[PMP_LOAD_RANGE_BUFFER_SIZE];
static uint32_t get_mepc(void) {
uint32_t mepc;
CSR_READ(CSR_REG_MEPC, &mepc);
return mepc;
}
static void set_mepc(uint32_t mepc) { CSR_WRITE(CSR_REG_MEPC, mepc); }
void handler_lsu_fault(void) {
pmp_load_exception = true;
uint32_t mepc = get_mepc();
LOG_INFO("Load fault exception handler: mepc = 0x%x", mepc);
// Check if the two least significant bits of the instruction are b11 (0x3),
// which means that the trapped instruction is not compressed
// (32bits = 4bytes), otherwise (16bits = 2bytes).
//
// NOTE:
// with RISC-V "c" (compressed instructions extension), 32bit
// instructions can start on 16bit boundary.
//
// Please see:
// "ā€œCā€ Standard Extension for Compressed Instructions, Version 2.0",
// section 16.1.
uint32_t fault_instruction = *((uint32_t *)mepc);
bool not_compressed = (fault_instruction & 0x3) == 0x3;
mepc = not_compressed ? (mepc + 4) : (mepc + 2);
set_mepc(mepc);
}
static void pmp_configure_load_napot(void) {
uintptr_t load_range_start =
(uintptr_t)&pmp_load_store_test_data[PMP_LOAD_RANGE_BOTTOM_OFFSET];
pmp_region_config_t config = {
.lock = kPmpRegionLockLocked,
.permissions = kPmpRegionPermissionsNone,
};
pmp_region_configure_napot_result_t result = pmp_region_configure_napot(
PMP_LOAD_REGION_ID, config, load_range_start, PMP_LOAD_RANGE_SIZE);
CHECK(result == kPmpRegionConfigureNapotOk,
"Load configuration failed, error code = %d", result);
}
const test_config_t kTestConfig = {
.can_clobber_uart = false,
};
bool test_main(void) {
pmp_load_exception = false;
char load = pmp_load_store_test_data[PMP_LOAD_RANGE_BOTTOM_OFFSET];
CHECK(!pmp_load_exception, "Load access violation before PMP configuration");
pmp_configure_load_napot();
pmp_load_exception = false;
load = pmp_load_store_test_data[PMP_LOAD_RANGE_BOTTOM_OFFSET];
CHECK(pmp_load_exception,
"No load access violation on the bottom of the range load");
pmp_load_exception = false;
load = pmp_load_store_test_data[PMP_LOAD_RANGE_TOP_OFFSET];
CHECK(pmp_load_exception,
"No load access violation on the top of the range load");
pmp_load_exception = false;
load = pmp_load_store_test_data[PMP_LOAD_RANGE_TOP_OFFSET + 1];
CHECK(!pmp_load_exception, "Load access violation on top of the range + 1");
(void)load;
return true;
}