Branch data Line data Source code
1 : : // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
2 : : /*
3 : : * NVRAM Format as specified in PAPR
4 : : *
5 : : * Copyright 2013-2026 IBM Corp.
6 : : */
7 : :
8 : : #include <skiboot.h>
9 : : #include <nvram.h>
10 : :
11 : : struct chrp_nvram_hdr {
12 : : uint8_t sig;
13 : : uint8_t cksum;
14 : : be16 len;
15 : : char name[12];
16 : : };
17 : :
18 : : static struct chrp_nvram_hdr *skiboot_part_hdr;
19 : :
20 : : #define NVRAM_SIG_FW_PRIV 0x51
21 : : #define NVRAM_SIG_SYSTEM 0x70
22 : : #define NVRAM_SIG_FREE 0x7f
23 : :
24 : : #define NVRAM_NAME_COMMON "common"
25 : : #define NVRAM_NAME_FW_PRIV "ibm,skiboot"
26 : : #define NVRAM_NAME_FREE "wwwwwwwwwwww"
27 : :
28 : : /* 64k should be enough, famous last words... */
29 : : #define NVRAM_SIZE_COMMON 0x10000
30 : :
31 : : /* 4k should be enough, famous last words... */
32 : : #define NVRAM_SIZE_FW_PRIV 0x1000
33 : :
34 : 82 : static uint8_t chrp_nv_cksum(struct chrp_nvram_hdr *hdr)
35 : : {
36 : 82 : struct chrp_nvram_hdr h_copy = *hdr;
37 : : uint8_t b_data, i_sum, c_sum;
38 : 82 : uint8_t *p = (uint8_t *)&h_copy;
39 : 82 : unsigned int nbytes = sizeof(h_copy);
40 : :
41 : 82 : h_copy.cksum = 0;
42 : 1394 : for (c_sum = 0; nbytes; nbytes--) {
43 : 1312 : b_data = *(p++);
44 : 1312 : i_sum = c_sum + b_data;
45 : 1312 : if (i_sum < c_sum)
46 : 345 : i_sum++;
47 : 1312 : c_sum = i_sum;
48 : : }
49 : 82 : return c_sum;
50 : : }
51 : :
52 : 17 : int nvram_format(void *nvram_image, uint32_t nvram_size)
53 : : {
54 : : struct chrp_nvram_hdr *h;
55 : 17 : unsigned int offset = 0;
56 : :
57 : 17 : prerror("NVRAM: Re-initializing (size: 0x%08x)\n", nvram_size);
58 : 17 : memset(nvram_image, 0, nvram_size);
59 : :
60 : : /* Create private partition */
61 : 17 : if (nvram_size - offset < NVRAM_SIZE_FW_PRIV)
62 : 1 : return -1;
63 : 16 : h = nvram_image + offset;
64 : 16 : h->sig = NVRAM_SIG_FW_PRIV;
65 : 16 : h->len = cpu_to_be16(NVRAM_SIZE_FW_PRIV >> 4);
66 : 16 : strcpy(h->name, NVRAM_NAME_FW_PRIV);
67 : 16 : h->cksum = chrp_nv_cksum(h);
68 : 16 : prlog(PR_DEBUG, "NVRAM: Created '%s' partition at 0x%08x"
69 : : " for size 0x%08x with cksum 0x%02x\n",
70 : : NVRAM_NAME_FW_PRIV, offset,
71 : : be16_to_cpu(h->len), h->cksum);
72 : 16 : offset += NVRAM_SIZE_FW_PRIV;
73 : :
74 : : /* Create common partition */
75 : 16 : if (nvram_size - offset < NVRAM_SIZE_COMMON)
76 : 2 : return -1;
77 : 14 : h = nvram_image + offset;
78 : 14 : h->sig = NVRAM_SIG_SYSTEM;
79 : 14 : h->len = cpu_to_be16(NVRAM_SIZE_COMMON >> 4);
80 : 14 : strcpy(h->name, NVRAM_NAME_COMMON);
81 : 14 : h->cksum = chrp_nv_cksum(h);
82 : 14 : prlog(PR_DEBUG, "NVRAM: Created '%s' partition at 0x%08x"
83 : : " for size 0x%08x with cksum 0x%02x\n",
84 : : NVRAM_NAME_COMMON, offset,
85 : : be16_to_cpu(h->len), h->cksum);
86 : 14 : offset += NVRAM_SIZE_COMMON;
87 : :
88 : : /* Create free space partition */
89 : 14 : if (nvram_size - offset < sizeof(struct chrp_nvram_hdr))
90 : 1 : return -1;
91 : 13 : h = nvram_image + offset;
92 : 13 : h->sig = NVRAM_SIG_FREE;
93 : 13 : h->len = cpu_to_be16((nvram_size - offset) >> 4);
94 : : /* We have the full 12 bytes here */
95 : 13 : memcpy(h->name, NVRAM_NAME_FREE, 12);
96 : 13 : h->cksum = chrp_nv_cksum(h);
97 : 13 : prlog(PR_DEBUG, "NVRAM: Created '%s' partition at 0x%08x"
98 : : " for size 0x%08x with cksum 0x%02x\n",
99 : : NVRAM_NAME_FREE, offset, be16_to_cpu(h->len), h->cksum);
100 : 13 : return 0;
101 : : }
102 : :
103 : : /*
104 : : * Check that the nvram partition layout is sane and that it
105 : : * contains our required partitions. If not, we re-format the
106 : : * lot of it
107 : : */
108 : 14 : int nvram_check(void *nvram_image, const uint32_t nvram_size)
109 : : {
110 : 14 : unsigned int offset = 0;
111 : 14 : bool found_common = false;
112 : :
113 : 14 : skiboot_part_hdr = NULL;
114 : :
115 : 43 : while (offset + sizeof(struct chrp_nvram_hdr) < nvram_size) {
116 : 33 : struct chrp_nvram_hdr *h = nvram_image + offset;
117 : :
118 : 33 : if (chrp_nv_cksum(h) != h->cksum) {
119 : 2 : prerror("NVRAM: Partition at offset 0x%x"
120 : : " has bad checksum: 0x%02x vs 0x%02x\n",
121 : : offset, h->cksum, chrp_nv_cksum(h));
122 : 2 : goto failed;
123 : : }
124 : : /*
125 : : * Length can't be zero or 1; need a length of at least 2 to account
126 : : * for the size of the chrp_nvram_hdr itself
127 : : */
128 : 31 : if (be16_to_cpu(h->len) < 2) {
129 : 1 : prerror("NVRAM: Partition at offset 0x%x has incorrect length of 0x%x\n",
130 : : offset, be16_to_cpu(h->len));
131 : 1 : goto failed;
132 : : }
133 : :
134 : 30 : if (h->sig == NVRAM_SIG_SYSTEM &&
135 : 10 : strcmp(h->name, NVRAM_NAME_COMMON) == 0)
136 : 9 : found_common = true;
137 : :
138 : 30 : if (h->sig == NVRAM_SIG_FW_PRIV &&
139 : 11 : strcmp(h->name, NVRAM_NAME_FW_PRIV) == 0)
140 : 10 : skiboot_part_hdr = h;
141 : :
142 : 30 : offset += be16_to_cpu(h->len) << 4;
143 : 30 : if (offset > nvram_size) {
144 : 1 : prerror("NVRAM: Partition at offset 0x%x"
145 : : " extends beyond end of nvram !\n", offset);
146 : 1 : goto failed;
147 : : }
148 : : }
149 : 10 : if (!found_common) {
150 : 1 : prlog_once(PR_ERR, "NVRAM: Common partition not found !\n");
151 : 1 : goto failed;
152 : : }
153 : :
154 : 9 : if (!skiboot_part_hdr) {
155 : 1 : prlog_once(PR_ERR, "NVRAM: Skiboot private partition not found !\n");
156 : 1 : goto failed;
157 : : } else {
158 : : /*
159 : : * The OF NVRAM format requires config strings to be NUL
160 : : * terminated and unused memory to be set to zero. Well behaved
161 : : * software should ensure this is done for us, but we should
162 : : * always check.
163 : : */
164 : 16 : const char *last_byte = (const char *) skiboot_part_hdr +
165 : 8 : be16_to_cpu(skiboot_part_hdr->len) * 16 - 1;
166 : :
167 : 8 : if (*last_byte != 0) {
168 : 1 : prerror("NVRAM: Skiboot private partition is not NUL terminated");
169 : 1 : goto failed;
170 : : }
171 : : }
172 : :
173 : 7 : prlog(PR_INFO, "NVRAM: Layout appears sane\n");
174 : 7 : assert(skiboot_part_hdr);
175 : 7 : return 0;
176 : 7 : failed:
177 : 7 : return -1;
178 : : }
179 : :
180 : 7 : static const char *find_next_key(const char *start, const char *end)
181 : : {
182 : : /*
183 : : * Unused parts of the partition are set to NUL. If we hit two
184 : : * NULs in a row then we assume that we have hit the end of the
185 : : * partition.
186 : : */
187 : 7 : if (*start == 0)
188 : 3 : return NULL;
189 : :
190 : 9 : while (start < end) {
191 : 9 : if (*start == 0)
192 : 4 : return start + 1;
193 : :
194 : 5 : start++;
195 : : }
196 : :
197 : 0 : return NULL;
198 : : }
199 : :
200 : 0 : static void nvram_dangerous(const char *key)
201 : : {
202 : 0 : prlog(PR_ERR, " ___________________________________________________________\n");
203 : 0 : prlog(PR_ERR, "< Dangerous NVRAM option: %s\n", key);
204 : 0 : prlog(PR_ERR, " -----------------------------------------------------------\n");
205 : 0 : prlog(PR_ERR, " \\ \n");
206 : 0 : prlog(PR_ERR, " \\ WW \n");
207 : 0 : prlog(PR_ERR, " <^ \\___/| \n");
208 : 0 : prlog(PR_ERR, " \\ / \n");
209 : 0 : prlog(PR_ERR, " \\_ _/ \n");
210 : 0 : prlog(PR_ERR, " }{ \n");
211 : 0 : }
212 : :
213 : :
214 : : /*
215 : : * nvram_query_safe/dangerous() - Searches skiboot NVRAM partition
216 : : * for a key=value pair.
217 : : *
218 : : * Note: nvram_check() must have already been called to set skiboot_part_hdr
219 : : *
220 : : * Dangerous means it should only be used for testing as it may
221 : : * mask issues. Safe is ok for long term use.
222 : : *
223 : : * Returns a pointer to a NUL terminated string that contains the value
224 : : * associated with the given key.
225 : : */
226 : 5 : static const char *__nvram_query(const char *key, bool dangerous)
227 : : {
228 : : const char *part_end, *start;
229 : 5 : int key_len = strlen(key);
230 : :
231 : 5 : assert(key);
232 : :
233 : 5 : if (!nvram_has_loaded()) {
234 : 0 : prlog(PR_DEBUG,
235 : : "NVRAM: Query for '%s' must wait for NVRAM to load\n",
236 : : key);
237 : 0 : if (!nvram_wait_for_load()) {
238 : 0 : prlog(PR_CRIT, "NVRAM: Failed to load\n");
239 : 0 : return NULL;
240 : : }
241 : : }
242 : :
243 : : /*
244 : : * The running OS can modify the NVRAM as it pleases so we need to be
245 : : * a little paranoid and check that it's ok before we try parse it.
246 : : *
247 : : * NB: nvram_validate() can update skiboot_part_hdr
248 : : */
249 : 5 : if (!nvram_validate())
250 : 0 : return NULL;
251 : :
252 : 5 : assert(skiboot_part_hdr);
253 : :
254 : : /*
255 : : * The end of the skiboot section is from the start of the section
256 : : * to len * 16 - 1
257 : : */
258 : 10 : part_end = (const char *) skiboot_part_hdr
259 : 5 : + be16_to_cpu(skiboot_part_hdr->len) * 16 - 1;
260 : :
261 : : /* Start just past the header */
262 : 5 : start = (const char *) skiboot_part_hdr
263 : : + sizeof(*skiboot_part_hdr);
264 : :
265 : : /*
266 : : * To account for sizeof(*skiboot_part_hdr), the len must be at least 2 to
267 : : * put part_end after start
268 : : */
269 : 5 : if ((be16_to_cpu(skiboot_part_hdr->len) < 2) || (start > part_end)) {
270 : 0 : prlog(PR_WARNING, "NVRAM: skiboot section length 0x%x is too small\n",
271 : : be16_to_cpu(skiboot_part_hdr->len));
272 : 0 : return NULL;
273 : : }
274 : :
275 : 5 : if (!key_len) {
276 : 0 : prlog(PR_WARNING, "NVRAM: search key is empty!\n");
277 : 0 : return NULL;
278 : : }
279 : :
280 : 5 : if (key_len > 32)
281 : 0 : prlog(PR_WARNING, "NVRAM: search key '%s' is longer than 32 chars\n", key);
282 : :
283 : 12 : while (start) {
284 : 9 : int remaining = part_end - start;
285 : :
286 : 9 : if (key_len + 1 > remaining)
287 : 0 : return NULL;
288 : :
289 : 9 : prlog(PR_TRACE, "NVRAM: '%s' (%lu)\n", start, strlen(start));
290 : :
291 : 9 : if (!strncmp(key, start, key_len) && start[key_len] == '=') {
292 : 2 : const char *value = &start[key_len + 1];
293 : :
294 : 2 : prlog(PR_DEBUG, "NVRAM: Searched for '%s' found '%s'\n",
295 : : key, value);
296 : :
297 : 2 : if (dangerous)
298 : 0 : nvram_dangerous(start);
299 : 2 : return value;
300 : : }
301 : :
302 : 7 : start = find_next_key(start, part_end);
303 : : }
304 : :
305 : 3 : prlog(PR_DEBUG, "NVRAM: '%s' not found\n", key);
306 : :
307 : 3 : return NULL;
308 : : }
309 : :
310 : 5 : const char *nvram_query_safe(const char *key)
311 : : {
312 : 5 : return __nvram_query(key, false);
313 : : }
314 : :
315 : 0 : const char *nvram_query_dangerous(const char *key)
316 : : {
317 : 0 : return __nvram_query(key, true);
318 : : }
319 : :
320 : : /*
321 : : * nvram_query_eq_safe/dangerous() - Check if the given 'key' exists
322 : : * and is set to 'value'.
323 : : *
324 : : * Dangerous means it should only be used for testing as it may
325 : : * mask issues. Safe is ok for long term use.
326 : : *
327 : : * Note: Its an error to check for non-existence of a key
328 : : * by passing 'value == NULL' as a key's value can never be
329 : : * NULL in nvram.
330 : : */
331 : 0 : static bool __nvram_query_eq(const char *key, const char *value, bool dangerous)
332 : : {
333 : 0 : const char *s = __nvram_query(key, dangerous);
334 : :
335 : 0 : if (!s)
336 : 0 : return false;
337 : :
338 : 0 : assert(value != NULL);
339 : 0 : return !strcmp(s, value);
340 : : }
341 : :
342 : 0 : bool nvram_query_eq_safe(const char *key, const char *value)
343 : : {
344 : 0 : return __nvram_query_eq(key, value, false);
345 : : }
346 : :
347 : 0 : bool nvram_query_eq_dangerous(const char *key, const char *value)
348 : : {
349 : 0 : return __nvram_query_eq(key, value, true);
350 : : }
351 : :
|