blob: ba7760dcd10d72e39ce81ceecac63a214987d48d [file] [log] [blame]
Aaron Durbindf3a1092013-03-13 12:41:44 -05001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2013 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 wacbmem_entryanty 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
Aaron Durbin40131cf2013-04-24 16:39:08 -050020#include <bootstate.h>
Aaron Durbindf3a1092013-03-13 12:41:44 -050021#include <boot/tables.h>
22#include <console/console.h>
23#include <cbmem.h>
24#include <string.h>
25#include <stdlib.h>
Aaron Durbin716738a2013-05-10 00:33:32 -050026#include <cpu/x86/car.h>
Aaron Durbindf3a1092013-03-13 12:41:44 -050027#if CONFIG_HAVE_ACPI_RESUME && !defined(__PRE_RAM__)
28#include <arch/acpi.h>
29#endif
30
31#ifndef UINT_MAX
32#define UINT_MAX 4294967295U
33#endif
34
35/* ACPI resume needs to be cleared in the fail-to-recover case, but that
36 * condition is only handled during ramstage. */
37#if CONFIG_HAVE_ACPI_RESUME && !defined(__PRE_RAM__)
38static inline void cbmem_handle_acpi_resume(void)
39{
40 /* Something went wrong, our high memory area got wiped */
41 if (acpi_slp_type == 3 || acpi_slp_type == 2)
42 acpi_slp_type = 0;
43}
44#else
45static inline void cbmem_handle_acpi_resume(void) {}
46#endif
47
48/*
49 * The dynamic cbmem code uses a root region. The root region boundary
50 * addresses are determined by cbmem_top() and ROOT_MIN_SIZE. Just below
51 * the address returned by cbmem_top() is a pointer that points to the
52 * root data structure. The root data structure provides the book keeping
53 * for each large entry.
54 */
55
56/* The root region is at least DYN_CBMEM_ALIGN_SIZE . */
57#define ROOT_MIN_SIZE DYN_CBMEM_ALIGN_SIZE
58#define CBMEM_POINTER_MAGIC 0xc0389479
59#define CBMEM_ENTRY_MAGIC ~(CBMEM_POINTER_MAGIC)
60
61/* The cbmem_root_pointer structure lives just below address returned
62 * from cbmem_top(). It points to the root data structure that
63 * maintains the entries. */
64struct cbmem_root_pointer {
65 u32 magic;
66 u32 root;
67} __attribute__((packed));
68
69struct cbmem_entry {
70 u32 magic;
71 u32 start;
72 u32 size;
73 u32 id;
74} __attribute__((packed));
75
76struct cbmem_root {
77 u32 max_entries;
78 u32 num_entries;
79 u32 locked;
80 u32 size;
81 struct cbmem_entry entries[0];
82} __attribute__((packed));
83
84
85static inline void *cbmem_top_cached(void)
86{
87#if !defined(__PRE_RAM__)
88 static void *cached_cbmem_top;
89
90 if (cached_cbmem_top == NULL)
91 cached_cbmem_top = cbmem_top();
92
93 return cached_cbmem_top;
94#else
95 return cbmem_top();
96#endif
97}
98
99static inline void *get_top_aligned(void)
100{
101 unsigned long top;
102
103 /* Align down what is returned from cbmem_top(). */
104 top = (unsigned long)cbmem_top_cached();
105 top &= ~(DYN_CBMEM_ALIGN_SIZE - 1);
106
107 return (void *)top;
108}
109
110static inline void *get_root(void)
111{
112 unsigned long pointer_addr;
113 struct cbmem_root_pointer *pointer;
114
115 pointer_addr = (unsigned long)get_top_aligned();
116 pointer_addr -= sizeof(struct cbmem_root_pointer);
117
118 pointer = (void *)pointer_addr;
119 if (pointer->magic != CBMEM_POINTER_MAGIC)
120 return NULL;
121
122 return (void *)pointer->root;
123}
124
125static inline void cbmem_entry_assign(struct cbmem_entry *entry,
126 u32 id, u32 start, u32 size)
127{
128 entry->magic = CBMEM_ENTRY_MAGIC;
129 entry->start = start;
130 entry->size = size;
131 entry->id = id;
132}
133
134static inline const struct cbmem_entry *
135cbmem_entry_append(struct cbmem_root *root, u32 id, u32 start, u32 size)
136{
137 struct cbmem_entry *cbmem_entry;
138
139 cbmem_entry = &root->entries[root->num_entries];
140 root->num_entries++;
141
142 cbmem_entry_assign(cbmem_entry, id, start, size);
143
144 return cbmem_entry;
145}
146
147void cbmem_initialize_empty(void)
148{
149 unsigned long pointer_addr;
150 unsigned long root_addr;
151 unsigned long max_entries;
152 struct cbmem_root *root;
153 struct cbmem_root_pointer *pointer;
154
155 /* Place the root pointer and the root. The number of entries is
156 * dictated by difference between the root address and the pointer
157 * where the root address is aligned down to
158 * DYN_CBMEM_ALIGN_SIZE. The pointer falls just below the
159 * address returned by get_top_aligned(). */
160 pointer_addr = (unsigned long)get_top_aligned();
161 root_addr = pointer_addr - ROOT_MIN_SIZE;
162 root_addr &= ~(DYN_CBMEM_ALIGN_SIZE - 1);
163 pointer_addr -= sizeof(struct cbmem_root_pointer);
164
165 max_entries = (pointer_addr - (root_addr + sizeof(*root))) /
166 sizeof(struct cbmem_entry);
167
168 pointer = (void *)pointer_addr;
169 pointer->magic = CBMEM_POINTER_MAGIC;
170 pointer->root = root_addr;
171
172 root = (void *)root_addr;
173 root->max_entries = max_entries;
174 root->num_entries = 0;
175 root->locked = 0;
176 root->size = pointer_addr - root_addr +
177 sizeof(struct cbmem_root_pointer);
178
179 /* Add an entry covering the root region. */
180 cbmem_entry_append(root, CBMEM_ID_ROOT, root_addr, root->size);
181
182 printk(BIOS_DEBUG, "CBMEM: root @ %p %d entries.\n",
183 root, root->max_entries);
184
185 cbmem_arch_init();
Aaron Durbin716738a2013-05-10 00:33:32 -0500186
187 /* Migrate cache-as-ram variables. */
188 car_migrate_variables();
Aaron Durbindf3a1092013-03-13 12:41:44 -0500189}
190
191static inline int cbmem_fail_recovery(void)
192{
193 cbmem_initialize_empty();
194 cbmem_handle_acpi_resume();
Aaron Durbin716738a2013-05-10 00:33:32 -0500195 /* Migrate cache-as-ram variables. */
196 car_migrate_variables();
Aaron Durbindf3a1092013-03-13 12:41:44 -0500197 return 1;
198}
199
200static int validate_entries(struct cbmem_root *root)
201{
202 unsigned int i;
203 u32 current_end;
204
205 current_end = (u32)get_top_aligned();
206
207 printk(BIOS_DEBUG, "CBMEM: recovering %d/%d entries from root @ %p\n",
208 root->num_entries, root->max_entries, root);
209
210 /* Check that all regions are properly aligned and are just below
211 * the previous entry */
212 for (i = 0; i < root->num_entries; i++) {
213 struct cbmem_entry *entry = &root->entries[i];
214
215 if (entry->magic != CBMEM_ENTRY_MAGIC)
216 return -1;
217
218 if (entry->start & (DYN_CBMEM_ALIGN_SIZE - 1))
219 return -1;
220
221 if (entry->start + entry->size != current_end)
222 return -1;
223
224 current_end = entry->start;
225 }
226
227 return 0;
228}
229
230int cbmem_initialize(void)
231{
232 struct cbmem_root *root;
233 void *top_according_to_root;
234
235 root = get_root();
236
237 /* No recovery possible since root couldn't be recovered. */
238 if (root == NULL)
239 return cbmem_fail_recovery();
240
241 /* Sanity check the root. */
242 top_according_to_root = (void *)(root->size + (unsigned long)root);
243 if (get_top_aligned() != top_according_to_root)
244 return cbmem_fail_recovery();
245
246 if (root->num_entries > root->max_entries)
247 return cbmem_fail_recovery();
248
249 if ((root->max_entries * sizeof(struct cbmem_entry)) >
250 (root->size - sizeof(struct cbmem_root_pointer) - sizeof(*root)))
251 return cbmem_fail_recovery();
252
253 /* Validate current entries. */
254 if (validate_entries(root))
255 return cbmem_fail_recovery();
256
257#if defined(__PRE_RAM__)
258 /* Lock the root in the romstage on a recovery. The assumption is that
259 * recovery is called during romstage on the S3 resume path. */
260 root->locked = 1;
261#endif
262
263 cbmem_arch_init();
264
Aaron Durbin716738a2013-05-10 00:33:32 -0500265 /* Migrate cache-as-ram variables. */
266 car_migrate_variables();
267
Aaron Durbindf3a1092013-03-13 12:41:44 -0500268 /* Recovery successful. */
269 return 0;
270}
271
272static void *cbmem_base(void)
273{
274 struct cbmem_root *root;
275 u32 low_addr;
276
277 root = get_root();
278
279 if (root == NULL)
280 return NULL;
281
282 low_addr = (u32)root;
283
284 /* Assume the lowest address is the last one added. */
285 if (root->num_entries > 0) {
286 low_addr = root->entries[root->num_entries - 1].start;
287 }
288
289 return (void *)low_addr;
290}
291
292
293const struct cbmem_entry *cbmem_entry_add(u32 id, u64 size64)
294{
295 struct cbmem_root *root;
296 const struct cbmem_entry *entry;
297 unsigned long base;;
298 u32 size;
299 u32 aligned_size;
300
301 entry = cbmem_entry_find(id);
302
303 if (entry != NULL)
304 return entry;
305
306 /* Only handle sizes <= UINT_MAX internally. */
307 if (size64 > (u64)UINT_MAX)
308 return NULL;
309
310 size = size64;
311
312 root = get_root();
313
314 if (root == NULL)
315 return NULL;
316
317 /* Nothing can be added once it is locked down. */
318 if (root->locked)
319 return NULL;
320
321 if (root->max_entries == root->num_entries)
322 return NULL;
323
324 aligned_size = ALIGN(size, DYN_CBMEM_ALIGN_SIZE);
325 base = (unsigned long)cbmem_base();
326 base -= aligned_size;
327
328 return cbmem_entry_append(root, id, base, aligned_size);
329}
330
331void *cbmem_add(u32 id, u64 size)
332{
333 const struct cbmem_entry *entry;
334
335 entry = cbmem_entry_add(id, size);
336
337 if (entry == NULL)
338 return NULL;
339
340 return cbmem_entry_start(entry);
341}
342
343/* Retrieve a region provided a given id. */
344const struct cbmem_entry *cbmem_entry_find(u32 id)
345{
346 struct cbmem_root *root;
347 const struct cbmem_entry *entry;
348 unsigned int i;
349
350 root = get_root();
351
352 if (root == NULL)
353 return NULL;
354
355 entry = NULL;
356
357 for (i = 0; i < root->num_entries; i++) {
358 if (root->entries[i].id == id) {
359 entry = &root->entries[i];
360 break;
361 }
362 }
363
364 return entry;
365}
366
367void *cbmem_find(u32 id)
368{
369 const struct cbmem_entry *entry;
370
371 entry = cbmem_entry_find(id);
372
373 if (entry == NULL)
374 return NULL;
375
376 return cbmem_entry_start(entry);
377}
378
379/* Remove a reserved region. Returns 0 on success, < 0 on error. Note: A region
380 * cannot be removed unless it was the last one added. */
381int cbmem_entry_remove(const struct cbmem_entry *entry)
382{
383 unsigned long entry_num;
384 struct cbmem_root *root;
385
386 root = get_root();
387
388 if (root == NULL)
389 return -1;
390
391 if (root->num_entries == 0)
392 return -1;
393
394 /* Nothing can be removed. */
395 if (root->locked)
396 return -1;
397
398 entry_num = entry - &root->entries[0];
399
400 /* If the entry is the last one in the root it can be removed. */
401 if (entry_num == (root->num_entries - 1)) {
402 root->num_entries--;
403 return 0;
404 }
405
406 return -1;
407}
408
409u64 cbmem_entry_size(const struct cbmem_entry *entry)
410{
411 return entry->size;
412}
413
414void *cbmem_entry_start(const struct cbmem_entry *entry)
415{
416 return (void *)entry->start;
417}
418
419
420#if !defined(__PRE_RAM__)
421/* selected cbmem can be initialized early in ramstage. Additionally, that
422 * means cbmem console can be reinitialized early as well. The post_device
423 * function is empty since cbmem was initialized early in ramstage. */
Aaron Durbin40131cf2013-04-24 16:39:08 -0500424static void init_cbmem_pre_device(void *unused)
Aaron Durbindf3a1092013-03-13 12:41:44 -0500425{
426 cbmem_initialize();
427#if CONFIG_CONSOLE_CBMEM
428 cbmemc_reinit();
429#endif /* CONFIG_CONSOLE_CBMEM */
430}
431
Aaron Durbin40131cf2013-04-24 16:39:08 -0500432BOOT_STATE_INIT_ENTRIES(cbmem_bscb) = {
433 BOOT_STATE_INIT_ENTRY(BS_PRE_DEVICE, BS_ON_ENTRY,
434 init_cbmem_pre_device, NULL),
435};
Aaron Durbindf3a1092013-03-13 12:41:44 -0500436
437void cbmem_add_lb_mem(struct lb_memory *mem)
438{
439 unsigned long base;
440 unsigned long top;
441
442 base = (unsigned long)cbmem_base();
443 top = (unsigned long)get_top_aligned();
444 lb_add_memory_range(mem, LB_MEM_TABLE, base, top - base);
445}
446
447void cbmem_list(void)
448{
449 unsigned int i;
450 struct cbmem_root *root;
451
452 root = get_root();
453
454 if (root == NULL)
455 return;
456
457 for (i = 0; i < root->num_entries; i++) {
458 struct cbmem_entry *entry;
459
460 entry = &root->entries[i];
461
462 cbmem_print_entry(i, entry->id, entry->start, entry->size);
463 }
464}
465#endif /* __PRE_RAM__ */