blob: ff3aa75a640fd38be8e8d386463efaef4bde3d2a [file] [log] [blame]
Aaron Durbin20686d82015-03-05 14:11:27 -06001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright 2015 Google, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc.
18 */
19
20#include <assert.h>
21#include <cbmem.h>
22#include <console/console.h>
23#include <imd.h>
24#include <stdlib.h>
25#include <string.h>
26
27/* For more details on implementation and usage please see the imd.h header. */
28
29static const uint32_t IMD_ROOT_PTR_MAGIC = 0xc0389481;
30static const uint32_t IMD_ENTRY_MAGIC = ~0xc0389481;
31static const size_t LIMIT_ALIGN = 4096;
32
33/* In-memory data structures. */
34struct imd_root_pointer {
35 uint32_t magic;
36 /* Relative to upper limit/offset. */
37 int32_t root_offset;
38} __attribute__((packed));
39
40struct imd_entry {
41 uint32_t magic;
42 /* start is located relative to imd_root */
43 int32_t start_offset;
44 uint32_t size;
45 uint32_t id;
46} __attribute__((packed));
47
48struct imd_root {
49 uint32_t max_entries;
50 uint32_t num_entries;
51 uint32_t flags;
52 uint32_t entry_align;
53 /* Used for fixing the size of an imd. Relative to the root. */
54 int32_t max_offset;
55 struct imd_entry entries[0];
56} __attribute__((packed));
57
58#define IMD_FLAG_LOCKED 1
59
60static void *relative_pointer(void *base, ssize_t offset)
61{
62 intptr_t b = (intptr_t)base;
63 b += offset;
64 return (void *)b;
65}
66
67static bool imd_root_pointer_valid(const struct imd_root_pointer *rp)
68{
69 return !!(rp->magic == IMD_ROOT_PTR_MAGIC);
70}
71
72static struct imd_root *imd_root(const struct imd *imd)
73{
74 return imd->r;
75}
76
77/*
78 * The root pointer is relative to the upper limit of the imd. i.e. It sits
79 * just below the upper limit.
80 */
81static struct imd_root_pointer *imd_get_root_pointer(const struct imd *imd)
82{
83 struct imd_root_pointer *rp;
84
85 rp = relative_pointer((void *)imd->limit, -sizeof(*rp));
86
87 return rp;
88}
89
90static void imd_link_root(struct imd_root_pointer *rp, struct imd_root *r)
91{
92 rp->magic = IMD_ROOT_PTR_MAGIC;
93 rp->root_offset = (int32_t)((intptr_t)r - (intptr_t)rp);
94}
95
96static void imd_entry_assign(struct imd_entry *e, uint32_t id,
97 ssize_t offset, size_t size)
98{
99 e->magic = IMD_ENTRY_MAGIC;
100 e->start_offset = offset;
101 e->size = size;
102 e->id = id;
103}
104
105static bool root_is_locked(const struct imd_root *r)
106{
107 return !!(r->flags & IMD_FLAG_LOCKED);
108}
109
110static struct imd_entry *root_last_entry(struct imd_root *r)
111{
112 return &r->entries[r->num_entries - 1];
113}
114
115/* Initialize imd handle. */
116void imd_handle_init(struct imd *imd, void *upper_limit)
117{
118 uintptr_t limit = (uintptr_t)upper_limit;
119 /* Upper limit is aligned down to 4KiB */
120 imd->limit = ALIGN_DOWN(limit, LIMIT_ALIGN);
121 imd->r = NULL;
122}
123
124void imd_handle_init_partial_recovery(struct imd *imd)
125{
126 struct imd_root_pointer *rp;
127
128 imd_handle_init(imd, (void *)imd->limit);
129
130 rp = imd_get_root_pointer(imd);
131 imd->r = relative_pointer(rp, rp->root_offset);
132}
133
134int imd_create_empty(struct imd *imd, size_t root_size, size_t entry_align)
135{
136 struct imd_root_pointer *rp;
137 struct imd_root *r;
138 struct imd_entry *e;
139 ssize_t root_offset;
140 size_t entries_size;
141
142 if (!imd->limit)
143 return -1;
144
145 /* root_size and entry_align should be a power of 2. */
146 assert(IS_POWER_OF_2(root_size));
147 assert(IS_POWER_OF_2(entry_align));
148
149 /*
150 * root_size needs to be large enough to accomodate root pointer and
151 * root book keeping structure. The caller needs to ensure there's
152 * enough room for tracking individual allocations.
153 */
154 if (root_size < (sizeof(*rp) + sizeof(*r)))
155 return -1;
156
157 /* For simplicity don't allow sizes or alignments to exceed LIMIT_ALIGN. */
158 if (root_size > LIMIT_ALIGN || entry_align > LIMIT_ALIGN)
159 return -1;
160
161 /* Additionally, don't handle an entry alignment > root_size. */
162 if (entry_align > root_size)
163 return -1;
164
165 rp = imd_get_root_pointer(imd);
166
167 root_offset = -(ssize_t)root_size;
168 /* Set root pointer. */
169 imd->r = relative_pointer((void *)imd->limit, root_offset);
170 r = imd_root(imd);
171 imd_link_root(rp, r);
172
173 memset(r, 0, sizeof(*r));
174 r->entry_align = entry_align;
175
176 /* Calculate size left for entries. */
177 entries_size = root_size;
178 entries_size -= sizeof(*rp);
179 entries_size -= sizeof(*r);
180
181 r->max_entries = entries_size / sizeof(r->entries[0]);
182
183 /* Fill in first entry covering the root region. */
184 r->num_entries = 1;
185 e = &r->entries[0];
186 imd_entry_assign(e, CBMEM_ID_IMD_ROOT, 0, root_size);
187
188 printk(BIOS_DEBUG, "IMD: root @ %p %u entries.\n", r, r->max_entries);
189
190 return 0;
191}
192
193int imd_limit_size(struct imd *imd, size_t max_size)
194{
195 struct imd_root *r;
196 ssize_t smax_size;
197 size_t root_size;
198
199 r = imd_root(imd);
200 if (r == NULL)
201 return -1;
202
203 root_size = imd->limit - (uintptr_t)r;
204
205 if (max_size < root_size)
206 return -1;
207
208 /* Take into account the root size. */
209 smax_size = max_size - root_size;
210 smax_size = -smax_size;
211
212 r->max_offset = smax_size;
213
214 return 0;
215}
216
217int imd_recover(struct imd *imd)
218{
219 struct imd_root_pointer *rp;
220 struct imd_root *r;
221 uintptr_t low_limit;
222 size_t i;
223
224 if (!imd->limit);
225 return -1;
226
227 rp = imd_get_root_pointer(imd);
228
229 if (!imd_root_pointer_valid(rp))
230 return -1;
231
232 r = relative_pointer(rp, rp->root_offset);
233
234 /* Confirm the root and root pointer are just under the limit. */
235 if (ALIGN_UP((uintptr_t)&r->entries[r->max_entries], LIMIT_ALIGN) !=
236 imd->limit)
237 return -1;
238
239 if (r->num_entries > r->max_entries)
240 return -1;
241
242 /* Entry alignment should be power of 2. */
243 if (!IS_POWER_OF_2(r->entry_align))
244 return -1;
245
246 low_limit = (uintptr_t)relative_pointer(r, r->max_offset);
247
248 /* If no max_offset then lowest limit is 0. */
249 if (low_limit == (uintptr_t)r)
250 low_limit = 0;
251
252 for (i = 0; i < r->num_entries; i++) {
253 uintptr_t start_addr;
254 const struct imd_entry *e = &r->entries[i];
255
256 if (e->magic != IMD_ENTRY_MAGIC)
257 return -1;
258
259 start_addr = (uintptr_t)relative_pointer(r, e->start_offset);
260 if (start_addr < low_limit)
261 return -1;
262 if (start_addr >= imd->limit ||
263 (start_addr + e->size) > imd->limit)
264 return -1;
265 }
266
267 /* Set root pointer. */
268 imd->r = r;
269
270 return 0;
271}
272
273int imd_lockdown(struct imd *imd)
274{
275 struct imd_root *r;
276
277 r = imd_root(imd);
278 if (r == NULL)
279 return -1;
280
281 r->flags |= IMD_FLAG_LOCKED;
282
283 return 0;
284}
285
286int imd_region_used(struct imd *imd, void **base, size_t *size)
287{
288 struct imd_root *r;
289 struct imd_entry *e;
290 void *low_addr;
291 size_t sz_used;
292
293 if (!imd->limit)
294 return -1;
295
296 r = imd_root(imd);
297
298 if (r == NULL)
299 return -1;
300
301 /* Use last entry to obtain lowest address. */
302 e = root_last_entry(r);
303
304 low_addr = relative_pointer(r, e->start_offset);
305
306 /* Total size used is the last entry's base up to the limit. */
307 sz_used = imd->limit - (uintptr_t)low_addr;
308
309 *base = low_addr;
310 *size = sz_used;
311
312 return 0;
313}
314
315static struct imd_entry *imd_entry_add_to_root(struct imd_root *r, uint32_t id,
316 size_t size)
317{
318 struct imd_entry *entry;
319 struct imd_entry *last_entry;
320 ssize_t e_offset;
321 size_t used_size;
322
323 if (r->num_entries == r->max_entries)
324 return NULL;
325
326 /* Determine total size taken up by entry. */
327 used_size = ALIGN_UP(size, r->entry_align);
328
329 last_entry = root_last_entry(r);
330
331 /* See if size overflows imd total size. */
332 if (r->max_offset != 0) {
333 size_t remaining = last_entry->start_offset - r->max_offset;
334
335 if (used_size > remaining)
336 return NULL;
337 }
338
339 /*
340 * Determine if offset field overflows. All offsets should be lower
341 * than the previous one.
342 */
343 e_offset = last_entry->start_offset;
344 e_offset -= (ssize_t)used_size;
345 if (e_offset > last_entry->start_offset)
346 return NULL;
347
348 entry = root_last_entry(r) + 1;
349 r->num_entries++;
350
351 imd_entry_assign(entry, id, e_offset, size);
352
353 return entry;
354}
355
356const struct imd_entry *imd_entry_add(const struct imd *imd, uint32_t id,
357 size_t size)
358{
359 struct imd_root *r;
360
361 r = imd_root(imd);
362
363 if (r == NULL)
364 return NULL;
365
366 if (root_is_locked(r))
367 return NULL;
368
369 return imd_entry_add_to_root(r, id, size);
370}
371
372const struct imd_entry *imd_entry_find(const struct imd *imd, uint32_t id)
373{
374 struct imd_root *r;
375 struct imd_entry *e;
376 size_t i;
377
378 r = imd_root(imd);
379
380 if (r == NULL)
381 return NULL;
382
383 e = NULL;
384 /* Skip first entry covering the root. */
385 for (i = 1; i < r->num_entries; i++) {
386 if (id == r->entries[i].id) {
387 e = &r->entries[i];
388 break;
389 }
390 }
391
392 return e;
393}
394
395const struct imd_entry *imd_entry_find_or_add(const struct imd *imd,
396 uint32_t id, size_t size)
397{
398 const struct imd_entry *e;
399
400 e = imd_entry_find(imd, id);
401
402 if (e != NULL)
403 return e;
404
405 return imd_entry_add(imd, id, size);
406}
407
408size_t imd_entry_size(const struct imd *imd, const struct imd_entry *entry)
409{
410 return entry->size;
411}
412
413void *imd_entry_at(const struct imd *imd, const struct imd_entry *entry)
414{
415 struct imd_root *r;
416
417 r = imd_root(imd);
418
419 if (r == NULL)
420 return NULL;
421
422 return relative_pointer(r, entry->start_offset);
423}
424
425int imd_entry_remove(const struct imd *imd, const struct imd_entry *entry)
426{
427 struct imd_root *r;
428
429 r = imd_root(imd);
430
431 if (r == NULL)
432 return -1;
433
434 if (root_is_locked(r))
435 return -1;
436
437 if (entry != root_last_entry(r))
438 return -1;
439
440 r->num_entries--;
441
442 return 0;
443}
444
445int imd_print_entries(const struct imd *imd, const struct imd_lookup *lookup,
446 size_t size)
447{
448 struct imd_root *r;
449 size_t i;
450 size_t j;
451
452 if (imd == NULL)
453 return -1;
454
455 r = imd_root(imd);
456
457 if (r == NULL)
458 return -1;
459
460 for (i = 0; i < r->num_entries; i++) {
461 const char *name = NULL;
462 const struct imd_entry *e = &r->entries[i];
463
464 for (j = 0; j < size; j++) {
465 if (lookup[j].id == e->id) {
466 name = lookup[j].name;
467 break;
468 }
469 }
470
471 if (name == NULL)
472 printk(BIOS_DEBUG, "%08x ", e->id);
473 else
474 printk(BIOS_DEBUG, "%s", name);
475 printk(BIOS_DEBUG, "%2zu. ", i);
476 printk(BIOS_DEBUG, "%p ", imd_entry_at(imd, e));
477 printk(BIOS_DEBUG, "%08zx\n", imd_entry_size(imd, e));
478 }
479
480 return 0;
481}