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
17enum {
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 */
36static 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
56void 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);
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;
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
89uint32_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
99void 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
109void 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
121void 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
142void 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
172void 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}