blob: 0336455da916e73719ced4bd2b21c0c5c3a1d858 [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/sca/lib/sca.h"
#include "sw/device/lib/arch/device.h"
#include "sw/device/lib/base/bitfield.h"
#include "sw/device/lib/dif/dif_clkmgr.h"
#include "sw/device/lib/dif/dif_csrng.h"
#include "sw/device/lib/dif/dif_edn.h"
#include "sw/device/lib/dif/dif_entropy_src.h"
#include "sw/device/lib/dif/dif_gpio.h"
#include "sw/device/lib/dif/dif_rv_timer.h"
#include "sw/device/lib/dif/dif_uart.h"
#include "sw/device/lib/handler.h"
#include "sw/device/lib/irq.h"
#include "sw/device/lib/pinmux.h"
#include "sw/device/lib/runtime/hart.h"
#include "sw/device/lib/runtime/print.h"
#include "clkmgr_regs.h" // Generated
#include "csrng_regs.h" // Generated
#include "edn_regs.h" // Generated
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
/**
* Macro for ignoring return values.
*
* This is needed because ‘(void)expr;` does not work for gcc.
*/
#define IGNORE_RESULT(expr) \
if (expr) { \
}
/**
* Bitfield for the trigger source.
*
* Bits 9 to 11 are used to select the trigger source. See chiplevel.sv.tpl for
* details.
*/
static const bitfield_field32_t kTriggerSourceBitfield = {
.index = 9,
.mask = 0x7,
};
enum {
/**
* Bit index of the trigger gate signal for gating the trigger from software.
*
* See chiplevel.sv.tpl for details.
*/
kTriggerGateBitIndex = 8,
/**
* RV timer settings.
*/
kRvTimerComparator = 0,
kRvTimerHart = kTopEarlgreyPlicTargetIbex0,
};
static dif_uart_t uart0;
static dif_uart_t uart1;
static dif_gpio_t gpio;
static dif_rv_timer_t timer;
static dif_csrng_t csrng;
static dif_edn_t edn0;
static dif_edn_t edn1;
// TODO(alphan): Handle return values as long as they don't affect capture rate.
/**
* Initializes the UART peripheral.
*/
static void sca_init_uart(void) {
const dif_uart_config_t uart_config = {
.baudrate = kUartBaudrate,
.clk_freq_hz = kClockFreqPeripheralHz,
.parity_enable = kDifToggleDisabled,
.parity = kDifUartParityEven,
};
IGNORE_RESULT(dif_uart_init(
mmio_region_from_addr(TOP_EARLGREY_UART0_BASE_ADDR), &uart0));
IGNORE_RESULT(dif_uart_configure(&uart0, uart_config));
base_uart_stdout(&uart0);
IGNORE_RESULT(dif_uart_init(
mmio_region_from_addr(TOP_EARLGREY_UART1_BASE_ADDR), &uart1));
IGNORE_RESULT(dif_uart_configure(&uart1, uart_config));
}
/**
* Initializes the GPIO peripheral.
*
* @param trigger Trigger source.
*/
static void sca_init_gpio(sca_trigger_source_t trigger) {
IGNORE_RESULT(
dif_gpio_init(mmio_region_from_addr(TOP_EARLGREY_GPIO_BASE_ADDR), &gpio));
uint32_t select_mask =
bitfield_field32_write(0, kTriggerSourceBitfield, UINT32_MAX);
uint32_t enable_mask = bitfield_bit32_write(0, kTriggerGateBitIndex, true);
IGNORE_RESULT(
dif_gpio_output_set_enabled_all(&gpio, select_mask | enable_mask));
IGNORE_RESULT(dif_gpio_write_masked(
&gpio, select_mask,
bitfield_field32_write(0, kTriggerSourceBitfield, trigger)));
}
/**
* Initializes the timer peripheral.
*/
static void sca_init_timer(void) {
IGNORE_RESULT(dif_rv_timer_init(
mmio_region_from_addr(TOP_EARLGREY_RV_TIMER_BASE_ADDR), &timer));
IGNORE_RESULT(dif_rv_timer_reset(&timer));
dif_rv_timer_tick_params_t tick_params;
IGNORE_RESULT(dif_rv_timer_approximate_tick_params(
kClockFreqPeripheralHz, kClockFreqCpuHz, &tick_params));
IGNORE_RESULT(
dif_rv_timer_set_tick_params(&timer, kRvTimerHart, tick_params));
IGNORE_RESULT(dif_rv_timer_irq_set_enabled(
&timer, kDifRvTimerIrqTimerExpiredHart0Timer0, kDifToggleEnabled));
irq_timer_ctrl(true);
irq_global_ctrl(true);
}
/**
* Initializes the CSRNG handle.
*/
static void sca_init_csrng(void) {
IGNORE_RESULT(dif_csrng_init(
mmio_region_from_addr(TOP_EARLGREY_CSRNG_BASE_ADDR), &csrng));
}
/**
* Initializes the EDN handle.
*/
static void sca_init_edn(void) {
IGNORE_RESULT(
dif_edn_init(mmio_region_from_addr(TOP_EARLGREY_EDN0_BASE_ADDR), &edn0));
IGNORE_RESULT(
dif_edn_init(mmio_region_from_addr(TOP_EARLGREY_EDN1_BASE_ADDR), &edn1));
}
/**
* Timer IRQ handler.
*
* Disables the counter and clears pending interrupts.
*/
void handler_irq_timer(void) {
// Return values of below functions are ignored to improve capture
// performance.
IGNORE_RESULT(dif_rv_timer_counter_set_enabled(&timer, kRvTimerHart,
kDifToggleDisabled));
IGNORE_RESULT(dif_rv_timer_irq_acknowledge(
&timer, kDifRvTimerIrqTimerExpiredHart0Timer0));
}
/**
* Disables the given peripherals to reduce noise during SCA.
*
* Care must be taken when disabling the entropy complex if a peripheral that
* depends on it will be used in SCA. E.g., We can disable the entropy complex
* when analyzing AES only because AES features a parameter to skip PRNG
* reseeding for SCA experiments. Without this parameter, AES would simply get
* stalled with a disabled entropy complex.
*
* @param disable Set of peripherals to disable.
*/
void sca_disable_peripherals(sca_peripherals_t disable) {
if (disable & kScaPeripheralEdn) {
IGNORE_RESULT(dif_edn_stop(&edn0));
IGNORE_RESULT(dif_edn_stop(&edn1));
}
if (disable & kScaPeripheralCsrng) {
IGNORE_RESULT(dif_csrng_stop(&csrng));
}
if (disable & kScaPeripheralEntropy) {
dif_entropy_src_t entropy;
IGNORE_RESULT(dif_entropy_src_init(
mmio_region_from_addr(TOP_EARLGREY_ENTROPY_SRC_BASE_ADDR), &entropy));
IGNORE_RESULT(dif_entropy_src_disable(&entropy));
}
// Disable HMAC, KMAC, OTBN and USB clocks through CLKMGR DIF.
dif_clkmgr_t clkmgr;
IGNORE_RESULT(dif_clkmgr_init(
mmio_region_from_addr(TOP_EARLGREY_CLKMGR_AON_BASE_ADDR), &clkmgr));
if (disable & kScaPeripheralAes) {
IGNORE_RESULT(dif_clkmgr_hintable_clock_set_hint(
&clkmgr, CLKMGR_CLK_HINTS_CLK_MAIN_AES_HINT_BIT, kDifToggleDisabled));
}
if (disable & kScaPeripheralHmac) {
IGNORE_RESULT(dif_clkmgr_hintable_clock_set_hint(
&clkmgr, CLKMGR_CLK_HINTS_CLK_MAIN_HMAC_HINT_BIT, kDifToggleDisabled));
}
if (disable & kScaPeripheralKmac) {
IGNORE_RESULT(dif_clkmgr_hintable_clock_set_hint(
&clkmgr, CLKMGR_CLK_HINTS_CLK_MAIN_KMAC_HINT_BIT, kDifToggleDisabled));
}
if (disable & kScaPeripheralOtbn) {
IGNORE_RESULT(dif_clkmgr_hintable_clock_set_hint(
&clkmgr, CLKMGR_CLK_HINTS_CLK_IO_DIV4_OTBN_HINT_BIT,
kDifToggleDisabled));
IGNORE_RESULT(dif_clkmgr_hintable_clock_set_hint(
&clkmgr, CLKMGR_CLK_HINTS_CLK_MAIN_OTBN_HINT_BIT, kDifToggleDisabled));
}
if (disable & kScaPeripheralUsb) {
IGNORE_RESULT(dif_clkmgr_gateable_clock_set_enabled(
&clkmgr, CLKMGR_CLK_ENABLES_CLK_USB_PERI_EN_BIT, kDifToggleDisabled));
}
}
void sca_init(sca_trigger_source_t trigger, sca_peripherals_t enable) {
pinmux_init();
sca_init_uart();
sca_init_gpio(trigger);
sca_init_timer();
sca_init_csrng();
sca_init_edn();
sca_disable_peripherals(~enable);
}
void sca_get_uart(const dif_uart_t **uart_out) { *uart_out = &uart1; }
void sca_set_trigger_high() {
IGNORE_RESULT(dif_gpio_write(&gpio, kTriggerGateBitIndex, true));
}
void sca_set_trigger_low() {
IGNORE_RESULT(dif_gpio_write(&gpio, kTriggerGateBitIndex, false));
}
void sca_call_and_sleep(sca_callee callee, uint32_t sleep_cycles) {
// Start timer to wake Ibex after the callee is done.
uint64_t current_time;
// Return values of below functions are ignored to improve capture
// performance.
IGNORE_RESULT(dif_rv_timer_counter_read(&timer, kRvTimerHart, &current_time));
IGNORE_RESULT(dif_rv_timer_arm(&timer, kRvTimerHart, kRvTimerComparator,
current_time + sleep_cycles));
IGNORE_RESULT(dif_rv_timer_counter_set_enabled(&timer, kRvTimerHart,
kDifToggleEnabled));
callee();
wait_for_interrupt();
}