Software APIs
lc_ctrl_fi.c
1 // Copyright lowRISC contributors (OpenTitan project).
2 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3 // SPDX-License-Identifier: Apache-2.0
4 
6 #include "sw/device/lib/base/status.h"
8 #include "sw/device/lib/dif/dif_rv_core_ibex.h"
10 #include "sw/device/lib/testing/rv_core_ibex_testutils.h"
11 #include "sw/device/lib/testing/test_framework/ujson_ottf.h"
12 #include "sw/device/lib/ujson/ujson.h"
13 #include "sw/device/tests/penetrationtests/firmware/lib/pentest_lib.h"
14 #include "sw/device/tests/penetrationtests/json/lc_ctrl_fi_commands.h"
15 
17 
18 // NOP macros.
19 #define NOP1 "addi x0, x0, 0\n"
20 #define NOP10 NOP1 NOP1 NOP1 NOP1 NOP1 NOP1 NOP1 NOP1 NOP1 NOP1
21 #define NOP100 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10
22 
23 static dif_rv_core_ibex_t rv_core_ibex;
24 static dif_lc_ctrl_t lc;
25 
26 status_t handle_lc_ctrl_fi_init(ujson_t *uj) {
27  penetrationtest_cpuctrl_t uj_data;
28  TRY(ujson_deserialize_penetrationtest_cpuctrl_t(uj, &uj_data));
29 
30  pentest_select_trigger_type(kPentestTriggerTypeSw);
31  // As we are using the software defined trigger, the first argument of
32  // pentest_init is not needed. kPentestTriggerSourceAes is selected as a
33  // placeholder.
34  pentest_init(kPentestTriggerSourceAes,
35  kPentestPeripheralIoDiv4 | kPentestPeripheralCsrng);
36 
37  // Configure the CPU for the pentest.
38  penetrationtest_device_info_t uj_output;
39  TRY(pentest_configure_cpu(
40  uj_data.icache_disable, uj_data.dummy_instr_disable,
41  uj_data.enable_jittery_clock, uj_data.enable_sram_readback,
42  &uj_output.clock_jitter_locked, &uj_output.clock_jitter_en,
43  &uj_output.sram_main_readback_locked, &uj_output.sram_ret_readback_locked,
44  &uj_output.sram_main_readback_en, &uj_output.sram_ret_readback_en));
45 
46  // Configure Ibex to allow reading ERR_STATUS register.
47  TRY(dif_rv_core_ibex_init(
49  &rv_core_ibex));
50 
51  // Configure LC Controller.
52  mmio_region_t lc_reg =
54  TRY(dif_lc_ctrl_init(lc_reg, &lc));
55 
56  // Configure the alert handler. Alerts triggered by IP blocks are captured
57  // and reported to the test.
58  pentest_configure_alert_handler();
59 
60  // Read device ID and return to host.
61  TRY(pentest_read_device_id(uj_output.device_id));
62  RESP_OK(ujson_serialize_penetrationtest_device_info_t, uj, &uj_output);
63 
64  return OK_STATUS();
65 }
66 
67 status_t handle_lc_ctrl_fi_runtime_corruption(ujson_t *uj) {
68  // Clear registered alerts in alert handler.
69  pentest_registered_alerts_t reg_alerts = pentest_get_triggered_alerts();
70  // Clear the AST recoverable alerts.
71  pentest_clear_sensor_recov_alerts();
72 
73  // Read LC CTRL to get reference values.
74  dif_lc_ctrl_state_t lc_state_ref;
75  uint8_t lc_count_ref;
76  TRY(dif_lc_ctrl_get_state(&lc, &lc_state_ref));
77  TRY(dif_lc_ctrl_get_attempts(&lc, &lc_count_ref));
78 
79  pentest_set_trigger_high();
80  asm volatile(NOP100);
81  asm volatile(NOP100);
82  asm volatile(NOP100);
83  pentest_set_trigger_low();
84 
85  // Get registered alerts from alert handler.
86  reg_alerts = pentest_get_triggered_alerts();
87  // Get fatal and recoverable AST alerts from sensor controller.
88  pentest_sensor_alerts_t sensor_alerts = pentest_get_sensor_alerts();
89 
90  // Check if we have managed to manipulate the LC Controller.
91  dif_lc_ctrl_state_t lc_state_cmp;
92  uint8_t lc_count_cmp;
93  TRY(dif_lc_ctrl_get_state(&lc, &lc_state_cmp));
94  TRY(dif_lc_ctrl_get_attempts(&lc, &lc_count_cmp));
95 
96  // Do the comparison. Return res = 0 if no mismatch was detected. 1 is
97  // returned if only the lc_state was manipulated. 2 if only the counter was
98  // manipulated. 3 if state and counter was manipulated.
99  lc_ctrl_fi_corruption_t uj_output;
100  uj_output.res = 0;
101  if (lc_state_cmp != lc_state_ref) {
102  uj_output.res = 1;
103  }
104 
105  if (lc_count_cmp != lc_count_ref) {
106  if (uj_output.res) {
107  uj_output.res = 3;
108  } else {
109  uj_output.res = 2;
110  }
111  }
112 
113  // Read ERR_STATUS register from Ibex.
114  dif_rv_core_ibex_error_status_t err_ibx;
115  TRY(dif_rv_core_ibex_get_error_status(&rv_core_ibex, &err_ibx));
116 
117  // Send result & ERR_STATUS to host.
118  uj_output.state = lc_state_cmp;
119  uj_output.counter = lc_count_cmp;
120  uj_output.err_status = err_ibx;
121  memcpy(uj_output.alerts, reg_alerts.alerts, sizeof(reg_alerts.alerts));
122  memcpy(uj_output.ast_alerts, sensor_alerts.alerts,
123  sizeof(sensor_alerts.alerts));
124  RESP_OK(ujson_serialize_lc_ctrl_fi_corruption_t, uj, &uj_output);
125 
126  return OK_STATUS();
127 }
128 
129 status_t handle_lc_ctrl_fi(ujson_t *uj) {
130  lc_ctrl_fi_subcommand_t cmd;
131  TRY(ujson_deserialize_lc_ctrl_fi_subcommand_t(uj, &cmd));
132  switch (cmd) {
133  case kLcCtrlFiSubcommandInit:
134  return handle_lc_ctrl_fi_init(uj);
135  case kLcCtrlFiSubcommandRuntimeCorruption:
136  return handle_lc_ctrl_fi_runtime_corruption(uj);
137  default:
138  LOG_ERROR("Unrecognized LC CTRL FI subcommand: %d", cmd);
139  return INVALID_ARGUMENT();
140  }
141  return OK_STATUS();
142 }