blob: 0397594ef05e38363aa7f60ec923132c4ff93717 [file] [log] [blame]
Aaron Durbin49048022014-02-18 21:55:02 -06001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2003-2004 Eric Biederman
5 * Copyright (C) 2005-2010 coresystems GmbH
6 * Copyright (C) 2014 Google Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
Aaron Durbin49048022014-02-18 21:55:02 -060016 */
17
18#include <console/console.h>
19#include <bootmem.h>
20#include <cbmem.h>
21#include <device/resource.h>
22#include <stdlib.h>
Patrick Rudolph23d62dd2018-04-12 10:36:57 +020023#include <symbols.h>
Patrick Rudolph9ab9db02018-04-05 09:14:51 +020024#include <assert.h>
Aaron Durbin49048022014-02-18 21:55:02 -060025
Aaron Durbin4677f6b2018-04-03 00:08:12 -060026static int initialized;
Patrick Rudolph23d62dd2018-04-12 10:36:57 +020027static int table_written;
Aaron Durbin49048022014-02-18 21:55:02 -060028static struct memranges bootmem;
Patrick Rudolph64049be2018-04-20 10:37:51 +020029static struct memranges bootmem_os;
Aaron Durbin49048022014-02-18 21:55:02 -060030
Aaron Durbin4677f6b2018-04-03 00:08:12 -060031static int bootmem_is_initialized(void)
32{
33 return initialized;
34}
35
Patrick Rudolph23d62dd2018-04-12 10:36:57 +020036static int bootmem_memory_table_written(void)
37{
38 return table_written;
39}
40
41/* Platform hook to add bootmem areas the platform / board controls. */
42void __attribute__((weak)) bootmem_platform_add_ranges(void)
43{
44}
45
Patrick Rudolph9ab9db02018-04-05 09:14:51 +020046/* Convert bootmem tag to LB_MEM tag */
47static uint32_t bootmem_to_lb_tag(const enum bootmem_type tag)
48{
49 switch (tag) {
50 case BM_MEM_RAM:
51 return LB_MEM_RAM;
52 case BM_MEM_RESERVED:
53 return LB_MEM_RESERVED;
54 case BM_MEM_ACPI:
55 return LB_MEM_ACPI;
56 case BM_MEM_NVS:
57 return LB_MEM_NVS;
58 case BM_MEM_UNUSABLE:
59 return LB_MEM_UNUSABLE;
60 case BM_MEM_VENDOR_RSVD:
61 return LB_MEM_VENDOR_RSVD;
Patrick Rudolph4c3da702019-07-07 13:10:56 +020062 case BM_MEM_OPENSBI:
63 return LB_MEM_RESERVED;
Ting Shendff29e02019-01-28 18:15:00 +080064 case BM_MEM_BL31:
65 return LB_MEM_RESERVED;
Patrick Rudolph9ab9db02018-04-05 09:14:51 +020066 case BM_MEM_TABLE:
67 return LB_MEM_TABLE;
68 default:
69 printk(BIOS_ERR, "ERROR: Unsupported tag %u\n", tag);
70 return LB_MEM_RESERVED;
71 }
72}
73
Aaron Durbin4677f6b2018-04-03 00:08:12 -060074static void bootmem_init(void)
Aaron Durbin49048022014-02-18 21:55:02 -060075{
76 const unsigned long cacheable = IORESOURCE_CACHEABLE;
77 const unsigned long reserved = IORESOURCE_RESERVE;
78 struct memranges *bm = &bootmem;
79
Aaron Durbin4677f6b2018-04-03 00:08:12 -060080 initialized = 1;
81
Aaron Durbin49048022014-02-18 21:55:02 -060082 /*
83 * Fill the memory map out. The order of operations is important in
84 * that each overlapping range will take over the next. Therefore,
85 * add cacheable resources as RAM then add the reserved resources.
86 */
Patrick Rudolph9ab9db02018-04-05 09:14:51 +020087 memranges_init(bm, cacheable, cacheable, BM_MEM_RAM);
88 memranges_add_resources(bm, reserved, reserved, BM_MEM_RESERVED);
Julius Wernerae05d092018-05-08 17:09:57 -070089 memranges_clone(&bootmem_os, bm);
Aaron Durbin49048022014-02-18 21:55:02 -060090
91 /* Add memory used by CBMEM. */
92 cbmem_add_bootmem();
Aaron Durbind4afa932016-04-19 17:25:59 -050093
Julius Werner7e0dea62019-02-20 18:39:22 -080094 bootmem_add_range((uintptr_t)_stack, REGION_SIZE(stack),
95 BM_MEM_RAMSTAGE);
96 bootmem_add_range((uintptr_t)_program, REGION_SIZE(program),
97 BM_MEM_RAMSTAGE);
Patrick Rudolph23d62dd2018-04-12 10:36:57 +020098
Aaron Durbind4afa932016-04-19 17:25:59 -050099 bootmem_arch_add_ranges();
Patrick Rudolph23d62dd2018-04-12 10:36:57 +0200100 bootmem_platform_add_ranges();
Aaron Durbin49048022014-02-18 21:55:02 -0600101}
102
Patrick Rudolph9ab9db02018-04-05 09:14:51 +0200103void bootmem_add_range(uint64_t start, uint64_t size,
104 const enum bootmem_type tag)
Aaron Durbin49048022014-02-18 21:55:02 -0600105{
Patrick Rudolph9ab9db02018-04-05 09:14:51 +0200106 assert(tag > BM_MEM_FIRST && tag < BM_MEM_LAST);
107 assert(bootmem_is_initialized());
Aaron Durbin4677f6b2018-04-03 00:08:12 -0600108
Patrick Rudolph9ab9db02018-04-05 09:14:51 +0200109 memranges_insert(&bootmem, start, size, tag);
Julius Wernerae05d092018-05-08 17:09:57 -0700110 if (tag <= BM_MEM_OS_CUTOFF) {
111 /* Can't change OS tables anymore after they are written out. */
112 assert(!bootmem_memory_table_written());
113 memranges_insert(&bootmem_os, start, size, tag);
114 };
Aaron Durbin49048022014-02-18 21:55:02 -0600115}
116
117void bootmem_write_memory_table(struct lb_memory *mem)
118{
119 const struct range_entry *r;
120 struct lb_memory_range *lb_r;
121
122 lb_r = &mem->map[0];
123
Aaron Durbin4677f6b2018-04-03 00:08:12 -0600124 bootmem_init();
Aaron Durbin49048022014-02-18 21:55:02 -0600125 bootmem_dump_ranges();
126
Patrick Rudolph64049be2018-04-20 10:37:51 +0200127 memranges_each_entry(r, &bootmem_os) {
Aaron Durbin49048022014-02-18 21:55:02 -0600128 lb_r->start = pack_lb64(range_entry_base(r));
129 lb_r->size = pack_lb64(range_entry_size(r));
Patrick Rudolph9ab9db02018-04-05 09:14:51 +0200130 lb_r->type = bootmem_to_lb_tag(range_entry_tag(r));
Aaron Durbin49048022014-02-18 21:55:02 -0600131
132 lb_r++;
133 mem->size += sizeof(struct lb_memory_range);
134 }
Patrick Rudolph23d62dd2018-04-12 10:36:57 +0200135
Patrick Rudolph23d62dd2018-04-12 10:36:57 +0200136 table_written = 1;
Aaron Durbin49048022014-02-18 21:55:02 -0600137}
138
139struct range_strings {
Patrick Rudolph9ab9db02018-04-05 09:14:51 +0200140 enum bootmem_type tag;
Aaron Durbin49048022014-02-18 21:55:02 -0600141 const char *str;
142};
143
144static const struct range_strings type_strings[] = {
Patrick Rudolph9ab9db02018-04-05 09:14:51 +0200145 { BM_MEM_RAM, "RAM" },
146 { BM_MEM_RESERVED, "RESERVED" },
147 { BM_MEM_ACPI, "ACPI" },
148 { BM_MEM_NVS, "NVS" },
149 { BM_MEM_UNUSABLE, "UNUSABLE" },
150 { BM_MEM_VENDOR_RSVD, "VENDOR RESERVED" },
Ting Shendff29e02019-01-28 18:15:00 +0800151 { BM_MEM_BL31, "BL31" },
Patrick Rudolph4c3da702019-07-07 13:10:56 +0200152 { BM_MEM_OPENSBI, "OPENSBI" },
Patrick Rudolph9ab9db02018-04-05 09:14:51 +0200153 { BM_MEM_TABLE, "CONFIGURATION TABLES" },
Julius Wernerae05d092018-05-08 17:09:57 -0700154 { BM_MEM_RAMSTAGE, "RAMSTAGE" },
155 { BM_MEM_PAYLOAD, "PAYLOAD" },
Aaron Durbin49048022014-02-18 21:55:02 -0600156};
157
Patrick Rudolph9ab9db02018-04-05 09:14:51 +0200158static const char *bootmem_range_string(const enum bootmem_type tag)
Aaron Durbin49048022014-02-18 21:55:02 -0600159{
160 int i;
161
162 for (i = 0; i < ARRAY_SIZE(type_strings); i++) {
163 if (type_strings[i].tag == tag)
164 return type_strings[i].str;
165 }
166
167 return "UNKNOWN!";
168}
169
170void bootmem_dump_ranges(void)
171{
172 int i;
173 const struct range_entry *r;
174
175 i = 0;
176 memranges_each_entry(r, &bootmem) {
177 printk(BIOS_DEBUG, "%2d. %016llx-%016llx: %s\n",
178 i, range_entry_base(r), range_entry_end(r) - 1,
179 bootmem_range_string(range_entry_tag(r)));
180 i++;
181 }
182}
183
Patrick Rudolph64049be2018-04-20 10:37:51 +0200184bool bootmem_walk_os_mem(range_action_t action, void *arg)
185{
186 const struct range_entry *r;
187
188 assert(bootmem_is_initialized());
189
190 memranges_each_entry(r, &bootmem_os) {
191 if (!action(r, arg))
192 return true;
193 }
194
195 return false;
196}
197
Patrick Rudolphc6536232018-04-10 09:34:29 +0200198bool bootmem_walk(range_action_t action, void *arg)
199{
200 const struct range_entry *r;
201
202 assert(bootmem_is_initialized());
203
204 memranges_each_entry(r, &bootmem) {
205 if (!action(r, arg))
206 return true;
207 }
208
209 return false;
210}
211
Ting Shen05532262019-01-28 17:22:22 +0800212int bootmem_region_targets_type(uint64_t start, uint64_t size,
213 enum bootmem_type dest_type)
Aaron Durbin49048022014-02-18 21:55:02 -0600214{
215 const struct range_entry *r;
Ting Shen05532262019-01-28 17:22:22 +0800216 uint64_t end = start + size;
Aaron Durbin49048022014-02-18 21:55:02 -0600217
Ting Shen05532262019-01-28 17:22:22 +0800218 memranges_each_entry(r, &bootmem) {
Aaron Durbin49048022014-02-18 21:55:02 -0600219 /* All further bootmem entries are beyond this range. */
220 if (end <= range_entry_base(r))
221 break;
222
223 if (start >= range_entry_base(r) && end <= range_entry_end(r)) {
Ting Shen05532262019-01-28 17:22:22 +0800224 if (range_entry_tag(r) == dest_type)
Aaron Durbin49048022014-02-18 21:55:02 -0600225 return 1;
226 }
227 }
228 return 0;
229}
230
231void *bootmem_allocate_buffer(size_t size)
232{
233 const struct range_entry *r;
234 const struct range_entry *region;
235 /* All allocated buffers fall below the 32-bit boundary. */
236 const resource_t max_addr = 1ULL << 32;
237 resource_t begin;
238 resource_t end;
239
Aaron Durbin4677f6b2018-04-03 00:08:12 -0600240 if (!bootmem_is_initialized()) {
Wim Vervoorn87c52802019-11-12 11:15:23 +0100241 printk(BIOS_ERR, "%s: lib uninitialized!\n", __func__);
Aaron Durbin4677f6b2018-04-03 00:08:12 -0600242 return NULL;
243 }
244
Aaron Durbin49048022014-02-18 21:55:02 -0600245 /* 4KiB alignment. */
246 size = ALIGN(size, 4096);
247 region = NULL;
248 memranges_each_entry(r, &bootmem) {
249 if (range_entry_size(r) < size)
250 continue;
251
Patrick Rudolph9ab9db02018-04-05 09:14:51 +0200252 if (range_entry_tag(r) != BM_MEM_RAM)
Aaron Durbin49048022014-02-18 21:55:02 -0600253 continue;
254
255 if (range_entry_base(r) >= max_addr)
256 continue;
257
258 end = range_entry_end(r);
259 if (end > max_addr)
260 end = max_addr;
261
262 if ((end - range_entry_base(r)) < size)
263 continue;
264
265 region = r;
266 }
267
268 if (region == NULL)
269 return NULL;
270
271 /* region now points to the highest usable region for the given size. */
Aaron Durbin49048022014-02-18 21:55:02 -0600272 end = range_entry_end(region);
273 if (end > max_addr)
274 end = max_addr;
275 begin = end - size;
276
Elyes HAOUAS0bc9f0b2019-12-06 18:07:33 +0100277 /* Mark buffer as unusable for future buffer use. */
Patrick Rudolph23d62dd2018-04-12 10:36:57 +0200278 bootmem_add_range(begin, size, BM_MEM_PAYLOAD);
Aaron Durbin49048022014-02-18 21:55:02 -0600279
280 return (void *)(uintptr_t)begin;
281}