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