blob: 65f2b36b610ada57767ebb8835fc05aada7a4faf [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 "scrambled_ecc32_mem_area.h"
#include <algorithm>
#include <cassert>
#include <iostream>
#include <sstream>
#include "scramble_model.h"
#include "sv_scoped.h"
// This is the maximum width of a nonce that's supported by the code in
// prim_util_get_scramble_key_nonce.svh
static const uint32_t kScrMaxNonceWidth = 320;
static const uint32_t kScrMaxNonceWidthByte = (kScrMaxNonceWidth + 7) / 8;
// Functions to convert from integer address to/from a little-endian vector of
// bytes, addr_width is given in bits
static std::vector<uint8_t> AddrIntToBytes(uint32_t addr, uint32_t addr_width) {
uint32_t addr_width_bytes = (addr_width + 7) / 8;
std::vector<uint8_t> addr_bytes(addr_width_bytes);
for (uint32_t i = 0; i < addr_width_bytes; ++i) {
addr_bytes[i] = addr & 0xff;
addr >>= 8;
}
return addr_bytes;
}
static uint32_t AddrBytesToInt(const std::vector<uint8_t> &addr) {
assert(addr.size() <= 4);
uint32_t addr_out = 0;
int cur_shift = 0;
for (uint8_t byte : addr) {
addr_out |= ((uint32_t)byte << cur_shift);
cur_shift += 8;
}
return addr_out;
}
// Converts svBitVecVal (bit[m:n] SV type) into a byte vector
static std::vector<uint8_t> ByteVecFromSV(svBitVecVal sv_val[],
uint32_t bytes) {
int shift = 0;
std::vector<uint8_t> vec(bytes);
for (uint32_t i = 0; i < bytes; ++i) {
vec[i] = (sv_val[i / 4] >> shift) & 0xff;
shift += 8;
if (shift == 32) {
shift = 0;
}
}
return vec;
}
// Analogous to the vbits SystemVerilog function from prim_util_pkg.sv. It
// calculates the number of bits needed to address size items.
static uint32_t vbits(uint32_t size) {
assert(size > 0);
if (size == 1) {
return 1;
}
size -= 1;
uint32_t width = 0;
while (size) {
width++;
size /= 2;
}
return width;
}
extern "C" {
int simutil_get_scramble_key(svBitVecVal *key);
int simutil_get_scramble_nonce(svBitVecVal *nonce);
}
std::vector<uint8_t> ScrambledEcc32MemArea::GetScrambleKey() const {
SVScoped scoped(scr_scope_);
svBitVecVal key_minibuf[((kPrinceWidthByte * 2) + 3) / 4];
if (!simutil_get_scramble_key(key_minibuf)) {
std::ostringstream oss;
oss << "Could not read key at scope " << scr_scope_;
throw std::runtime_error(oss.str());
}
return ByteVecFromSV(key_minibuf, kPrinceWidthByte * 2);
}
std::vector<uint8_t> ScrambledEcc32MemArea::GetScrambleNonce() const {
assert(GetNonceWidthByte() <= kScrMaxNonceWidthByte);
SVScoped scoped(scr_scope_);
svBitVecVal nonce_minibuf[(kScrMaxNonceWidthByte + 3) / 4];
if (!simutil_get_scramble_nonce((svBitVecVal *)nonce_minibuf)) {
std::ostringstream oss;
oss << "Could not read nonce at scope " << scr_scope_;
throw std::runtime_error(oss.str());
}
return ByteVecFromSV(nonce_minibuf, GetNonceWidthByte());
}
ScrambledEcc32MemArea::ScrambledEcc32MemArea(const std::string &scope,
uint32_t size, uint32_t width_32,
bool repeat_keystream)
: Ecc32MemArea(
SVScoped::join_sv_scopes(
scope, "u_prim_ram_1p_adv.u_mem.gen_generic.u_impl_generic"),
size, width_32),
scr_scope_(scope) {
addr_width_ = vbits(size);
repeat_keystream_ = repeat_keystream;
}
uint32_t ScrambledEcc32MemArea::GetPhysWidth() const {
return (GetWidthByte() / 4) * 39;
}
uint32_t ScrambledEcc32MemArea::GetPhysWidthByte() const {
return (GetPhysWidth() + 7) / 8;
}
uint32_t ScrambledEcc32MemArea::GetPrinceReplications() const {
if (repeat_keystream_) {
return 1;
} else {
return (GetPhysWidthByte() + 7) / 8;
}
}
uint32_t ScrambledEcc32MemArea::GetNonceWidth() const {
return GetPrinceReplications() * 64;
}
uint32_t ScrambledEcc32MemArea::GetNonceWidthByte() const {
return GetPrinceReplications() * 8;
}
void ScrambledEcc32MemArea::WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
const std::vector<uint8_t> &data,
size_t start_idx,
uint32_t dst_word) const {
// Compute integrity
Ecc32MemArea::WriteBuffer(buf, data, start_idx, dst_word);
ScrambleBuffer(buf, dst_word);
}
std::vector<uint8_t> ScrambledEcc32MemArea::ReadUnscrambled(
const uint8_t buf[SV_MEM_WIDTH_BYTES], uint32_t src_word) const {
std::vector<uint8_t> scrambled_data(buf, buf + GetPhysWidthByte());
return scramble_decrypt_data(
scrambled_data, GetPhysWidth(), 39, AddrIntToBytes(src_word, addr_width_),
addr_width_, GetScrambleNonce(), GetScrambleKey(), repeat_keystream_);
}
void ScrambledEcc32MemArea::ReadBuffer(std::vector<uint8_t> &data,
const uint8_t buf[SV_MEM_WIDTH_BYTES],
uint32_t src_word) const {
std::vector<uint8_t> unscrambled_data = ReadUnscrambled(buf, src_word);
// Strip integrity to give final result
Ecc32MemArea::ReadBuffer(data, &unscrambled_data[0], src_word);
}
void ScrambledEcc32MemArea::ReadBufferWithIntegrity(
EccWords &data, const uint8_t buf[SV_MEM_WIDTH_BYTES],
uint32_t src_word) const {
std::vector<uint8_t> unscrambled_data = ReadUnscrambled(buf, src_word);
Ecc32MemArea::ReadBufferWithIntegrity(data, &unscrambled_data[0], src_word);
}
void ScrambledEcc32MemArea::WriteBufferWithIntegrity(
uint8_t buf[SV_MEM_WIDTH_BYTES], const EccWords &data, size_t start_idx,
uint32_t dst_word) const {
Ecc32MemArea::WriteBufferWithIntegrity(buf, data, start_idx, dst_word);
ScrambleBuffer(buf, dst_word);
}
void ScrambledEcc32MemArea::ScrambleBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
uint32_t dst_word) const {
std::vector<uint8_t> scramble_buf(buf, buf + GetPhysWidthByte());
// Scramble data with integrity
scramble_buf = scramble_encrypt_data(
scramble_buf, GetPhysWidth(), 39, AddrIntToBytes(dst_word, addr_width_),
addr_width_, GetScrambleNonce(), GetScrambleKey(), repeat_keystream_);
// Copy scrambled data to write buffer
std::copy(scramble_buf.begin(), scramble_buf.end(), &buf[0]);
}
uint32_t ScrambledEcc32MemArea::ToPhysAddr(uint32_t logical_addr) const {
// Scramble logical address to get physical address
return AddrBytesToInt(scramble_addr(AddrIntToBytes(logical_addr, addr_width_),
addr_width_, GetScrambleNonce(),
GetNonceWidth()));
}