Software APIs
sec_mmio.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 
7 #include <assert.h>
8 
12 
13 // The context is declared as weak so that the ROM and ROM_EXT may
14 // override its location.
16 
17 enum {
18  // Value with good hamming weight used to mask the stored expected value.
19  kSecMmioMaskVal = 0x21692436u,
20 
21  // Constants used for hardened comparisons. kSecMmioValZero ==
22  // ~kSecMmioValOne.
23  kSecMmioValZero = 0x3ca5965a,
24  kSecMmioValOne = 0xc35a69a5,
25 
26  // Number of address-value pairs in the table.
27  kEntryCount = ARRAYSIZE(sec_mmio_ctx.addrs),
28 };
29 
30 /**
31  * Updates or inserts the register entry pointed to by MMIO `addr` with the
32  * given `value`.
33  *
34  * Increments the `sec_mmio_ctx.last_index`.
35  */
36 static void upsert_register(uint32_t addr, uint32_t value) {
37  const uint32_t last_index = sec_mmio_ctx.last_index;
38  uint32_t i = 0, j = last_index - 1;
39  for (; launder32(i) < last_index && launder32(j) < last_index; ++i, --j) {
40  if (launder32(sec_mmio_ctx.addrs[i]) == addr) {
41  sec_mmio_ctx.values[i] = value;
42  break;
43  }
44  }
45  if (launder32(i) == last_index && launder32(i) < kSecMmioRegFileSize) {
46  HARDENED_CHECK_EQ(j, UINT32_MAX);
47  sec_mmio_ctx.addrs[i] = addr;
48  sec_mmio_ctx.values[i] = value;
50  }
51  // The following checks serve as an additional fault detection mechanism.
53  HARDENED_CHECK_LT(i, kSecMmioRegFileSize);
54 }
55 
56 void sec_mmio_init(void) {
57  sec_mmio_ctx.last_index = launder32(0);
58  sec_mmio_ctx.write_count = launder32(0);
59  sec_mmio_ctx.check_count = launder32(0);
60  sec_mmio_ctx.expected_write_count = launder32(0);
61  uint32_t i = 0, r = kEntryCount - 1;
62  for (; launder32(i) < kEntryCount && launder32(r) < kEntryCount; ++i, --r) {
63  sec_mmio_ctx.addrs[i] = UINT32_MAX;
64  sec_mmio_ctx.values[i] = UINT32_MAX;
65  }
66  HARDENED_CHECK_EQ(i, kEntryCount);
67  HARDENED_CHECK_EQ(r, UINT32_MAX);
68  uint32_t check = kSecMmioValZero ^ sec_mmio_ctx.last_index;
69  check ^= sec_mmio_ctx.write_count;
70  check ^= sec_mmio_ctx.check_count;
72  HARDENED_CHECK_EQ(check, kSecMmioValZero);
73 }
74 
76  sec_mmio_ctx.check_count = launder32(0);
77  uint32_t i = sec_mmio_ctx.last_index,
78  r = kEntryCount - sec_mmio_ctx.last_index - 1;
79  for (; launder32(i) < kEntryCount && launder32(r) < kEntryCount; ++i, --r) {
80  sec_mmio_ctx.addrs[i] = UINT32_MAX;
81  sec_mmio_ctx.values[i] = UINT32_MAX;
82  }
83  HARDENED_CHECK_EQ(i, kEntryCount);
84  HARDENED_CHECK_EQ(r, UINT32_MAX);
86 }
87 
89 uint32_t sec_mmio_read32(uint32_t addr) {
90  uint32_t value = abs_mmio_read32(addr);
91  uint32_t masked_value = value ^ kSecMmioMaskVal;
92  barrier32(masked_value);
93  upsert_register(addr, masked_value);
94  HARDENED_CHECK_EQ((abs_mmio_read32(addr) ^ kSecMmioMaskVal), masked_value);
95 
96  return value;
97 }
98 
99 void sec_mmio_write32(uint32_t addr, uint32_t value) {
100  abs_mmio_write32(addr, value);
101  uint32_t masked_value = value ^ kSecMmioMaskVal;
102  barrier32(masked_value);
103  upsert_register(addr, masked_value);
104  HARDENED_CHECK_EQ((abs_mmio_read32(addr) ^ kSecMmioMaskVal), masked_value);
105 
107 }
108 
109 void sec_mmio_write32_shadowed(uint32_t addr, uint32_t value) {
110  // Shadowed registers require two writes.
111  abs_mmio_write32(addr, value);
112  abs_mmio_write32(addr, value);
113  uint32_t masked_value = value ^ kSecMmioMaskVal;
114  barrier32(masked_value);
115  upsert_register(addr, masked_value);
116  HARDENED_CHECK_EQ((abs_mmio_read32(addr) ^ kSecMmioMaskVal), masked_value);
117 
119 }
120 
121 void sec_mmio_check_values(uint32_t rnd_offset) {
122  const uint32_t last_index = sec_mmio_ctx.last_index;
123  // Pick a random starting offset.
124  uint32_t offset = ((uint64_t)rnd_offset * (uint64_t)last_index) >> 32;
125  enum { kStep = 1 };
126  uint32_t i = 0, r = last_index - 1;
127  for (; launder32(i) < last_index && launder32(r) < last_index; ++i, --r) {
128  uint32_t read_value = abs_mmio_read32(sec_mmio_ctx.addrs[offset]);
129  HARDENED_CHECK_EQ(read_value ^ kSecMmioMaskVal,
130  sec_mmio_ctx.values[offset]);
131  offset += kStep;
132  if (offset >= last_index) {
133  offset -= last_index;
134  }
135  }
136  // Check for loop completion.
137  HARDENED_CHECK_EQ(i, last_index);
138  HARDENED_CHECK_EQ(r, UINT32_MAX);
140 }
141 
142 void sec_mmio_check_values_except_otp(uint32_t rnd_offset, uint32_t otp_base) {
143  const uint32_t last_index = sec_mmio_ctx.last_index;
144  // Pick a random starting offset.
145  uint32_t offset = ((uint64_t)rnd_offset * (uint64_t)last_index) >> 32;
146  enum { kStep = 1 };
147  uint32_t i = 0, r = last_index - 1;
148  // Make sure the otp_base is 64K aligned and not zero.
149  HARDENED_CHECK_EQ(otp_base & 0x0000FFFF, 0);
150  HARDENED_CHECK_NE(otp_base & 0xFFFF0000, 0);
151  for (; launder32(i) < last_index && launder32(r) < last_index; ++i, --r) {
152  uint32_t address = sec_mmio_ctx.addrs[offset];
153  // Avoid the 64K aligned and 64K sized region at otp_base.
154  if ((address & 0xFFFF0000) != otp_base) {
155  uint32_t read_value = abs_mmio_read32(launder32(address));
156  HARDENED_CHECK_EQ(read_value ^ kSecMmioMaskVal,
157  sec_mmio_ctx.values[offset]);
158  } else {
159  HARDENED_CHECK_EQ(address & 0xFFFF0000, otp_base);
160  }
161  offset += kStep;
162  if (offset >= last_index) {
163  offset -= last_index;
164  }
165  }
166  // Check for loop completion.
167  HARDENED_CHECK_EQ(i, last_index);
168  HARDENED_CHECK_EQ(r, UINT32_MAX);
170 }
171 
172 void sec_mmio_check_counters(uint32_t expected_check_count) {
173  uint32_t result = launder32(kSecMmioValZero) ^ sec_mmio_ctx.write_count;
175 
176  // Check the expected write count. This is equivalent to
177  // sec_mmio_ctx.write_count == sec_mmio_ctx.expected_write_count
178  HARDENED_CHECK_EQ(result, kSecMmioValZero);
179 
180  // Check the expected check counts. This is equivalent to
181  // sec_mmio_ctx.check_count == expected_check_count. This check is expected to
182  // fail if the previous check failed.
183  result ^= sec_mmio_ctx.check_count;
184  result ^= expected_check_count;
185  HARDENED_CHECK_EQ(~launder32(result), kSecMmioValOne);
186 
188 }