blob: 987f1510d7b144cb65bee21087b36d6f495628bc [file]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include "sw/device/silicon_creator/lib/base/sec_mmio.h"
#include <assert.h>
#include "sw/device/lib/base/abs_mmio.h"
#include "sw/device/lib/base/hardened.h"
#include "sw/device/lib/base/macros.h"
// The context is declared as weak so that the ROM and ROM_EXT may
// override its location.
OT_WEAK sec_mmio_ctx_t sec_mmio_ctx;
enum {
// Value with good hamming weight used to mask the stored expected value.
kSecMmioMaskVal = 0x21692436u,
// Constants used for hardened comparisons. kSecMmioValZero ==
// ~kSecMmioValOne.
kSecMmioValZero = 0x3ca5965a,
kSecMmioValOne = 0xc35a69a5,
};
/**
* Updates or inserts the register entry pointed to by MMIO `addr` with the
* given `value`.
*
* Increments the `sec_mmio_ctx.last_index`.
*/
static void upsert_register(uint32_t addr, uint32_t value) {
const size_t last_index = sec_mmio_ctx.last_index;
size_t i = 0;
for (; launder32(i) < last_index; ++i) {
if (sec_mmio_ctx.addrs[i] == addr) {
sec_mmio_ctx.values[i] = value;
break;
}
}
if (launder32(i) == last_index && launder32(i) < kSecMmioRegFileSize) {
sec_mmio_ctx.addrs[i] = addr;
sec_mmio_ctx.values[i] = value;
++sec_mmio_ctx.last_index;
}
// The following condition check serves as an additional fault detection
// mechanism.
HARDENED_CHECK_LT(i, kSecMmioRegFileSize);
}
void sec_mmio_init(void) {
sec_mmio_ctx.last_index = launder32(0);
sec_mmio_ctx.write_count = launder32(0);
sec_mmio_ctx.check_count = launder32(0);
sec_mmio_ctx.expected_write_count = launder32(0);
for (size_t i = 0; i < ARRAYSIZE(sec_mmio_ctx.addrs); ++i) {
sec_mmio_ctx.addrs[i] = UINT32_MAX;
sec_mmio_ctx.values[i] = UINT32_MAX;
}
uint32_t check = kSecMmioValZero ^ sec_mmio_ctx.last_index;
check ^= sec_mmio_ctx.write_count;
check ^= sec_mmio_ctx.check_count;
check ^= sec_mmio_ctx.expected_write_count;
HARDENED_CHECK_EQ(check, kSecMmioValZero);
}
void sec_mmio_next_stage_init(void) {
sec_mmio_ctx.check_count = launder32(0);
for (size_t i = sec_mmio_ctx.last_index; i < ARRAYSIZE(sec_mmio_ctx.addrs);
++i) {
sec_mmio_ctx.addrs[i] = UINT32_MAX;
sec_mmio_ctx.values[i] = UINT32_MAX;
}
HARDENED_CHECK_EQ(sec_mmio_ctx.check_count, 0);
}
uint32_t sec_mmio_read32(uint32_t addr) {
uint32_t value = abs_mmio_read32(addr);
uint32_t masked_value = value ^ kSecMmioMaskVal;
barrier32(masked_value);
upsert_register(addr, masked_value);
HARDENED_CHECK_EQ((abs_mmio_read32(addr) ^ kSecMmioMaskVal), masked_value);
return value;
}
void sec_mmio_write32(uint32_t addr, uint32_t value) {
abs_mmio_write32(addr, value);
uint32_t masked_value = value ^ kSecMmioMaskVal;
barrier32(masked_value);
upsert_register(addr, masked_value);
HARDENED_CHECK_EQ((abs_mmio_read32(addr) ^ kSecMmioMaskVal), masked_value);
++sec_mmio_ctx.write_count;
}
void sec_mmio_write32_shadowed(uint32_t addr, uint32_t value) {
// Shadowed registers require two writes.
abs_mmio_write32(addr, value);
abs_mmio_write32(addr, value);
uint32_t masked_value = value ^ kSecMmioMaskVal;
barrier32(masked_value);
upsert_register(addr, masked_value);
HARDENED_CHECK_EQ((abs_mmio_read32(addr) ^ kSecMmioMaskVal), masked_value);
++sec_mmio_ctx.write_count;
}
void sec_mmio_check_values(uint32_t rnd_offset) {
const uint32_t last_index = sec_mmio_ctx.last_index;
// Pick a random starting offset.
uint32_t offset = ((uint64_t)rnd_offset * (uint64_t)last_index) >> 32;
enum { kStep = 1 };
size_t i;
for (i = 0; launder32(i) < last_index; ++i) {
uint32_t read_value = abs_mmio_read32(sec_mmio_ctx.addrs[offset]);
HARDENED_CHECK_EQ(read_value ^ kSecMmioMaskVal,
sec_mmio_ctx.values[offset]);
offset += kStep;
if (offset >= last_index) {
offset -= last_index;
}
}
// Check for loop completion.
HARDENED_CHECK_EQ(i, last_index);
++sec_mmio_ctx.check_count;
}
void sec_mmio_check_counters(uint32_t expected_check_count) {
uint32_t result = launder32(kSecMmioValZero) ^ sec_mmio_ctx.write_count;
result ^= sec_mmio_ctx.expected_write_count;
// Check the expected write count. This is equivalent to
// sec_mmio_ctx.write_count == sec_mmio_ctx.expected_write_count
HARDENED_CHECK_EQ(result, kSecMmioValZero);
// Check the expected check counts. This is equivalent to
// sec_mmio_ctx.check_count == expected_check_count. This check is expected to
// fail if the previous check failed.
result ^= sec_mmio_ctx.check_count;
result ^= expected_check_count;
HARDENED_CHECK_EQ(~launder32(result), kSecMmioValOne);
++sec_mmio_ctx.check_count;
}