blob: ea02a16ddc6f2536de2cf2b6484653cbcc4e1a8f [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>
23
24static struct memranges bootmem;
25
26void bootmem_init(void)
27{
28 const unsigned long cacheable = IORESOURCE_CACHEABLE;
29 const unsigned long reserved = IORESOURCE_RESERVE;
30 struct memranges *bm = &bootmem;
31
32 /*
33 * Fill the memory map out. The order of operations is important in
34 * that each overlapping range will take over the next. Therefore,
35 * add cacheable resources as RAM then add the reserved resources.
36 */
37 memranges_init(bm, cacheable, cacheable, LB_MEM_RAM);
38 memranges_add_resources(bm, reserved, reserved, LB_MEM_RESERVED);
39
40 /* Add memory used by CBMEM. */
41 cbmem_add_bootmem();
Aaron Durbind4afa932016-04-19 17:25:59 -050042
43 bootmem_arch_add_ranges();
Aaron Durbin49048022014-02-18 21:55:02 -060044}
45
46void bootmem_add_range(uint64_t start, uint64_t size, uint32_t type)
47{
48 memranges_insert(&bootmem, start, size, type);
49}
50
51void bootmem_write_memory_table(struct lb_memory *mem)
52{
53 const struct range_entry *r;
54 struct lb_memory_range *lb_r;
55
56 lb_r = &mem->map[0];
57
58 bootmem_dump_ranges();
59
60 memranges_each_entry(r, &bootmem) {
61 lb_r->start = pack_lb64(range_entry_base(r));
62 lb_r->size = pack_lb64(range_entry_size(r));
63 lb_r->type = range_entry_tag(r);
64
65 lb_r++;
66 mem->size += sizeof(struct lb_memory_range);
67 }
68}
69
70struct range_strings {
71 unsigned long tag;
72 const char *str;
73};
74
75static const struct range_strings type_strings[] = {
76 { LB_MEM_RAM, "RAM" },
77 { LB_MEM_RESERVED, "RESERVED" },
78 { LB_MEM_ACPI, "ACPI" },
79 { LB_MEM_NVS, "NVS" },
80 { LB_MEM_UNUSABLE, "UNUSABLE" },
81 { LB_MEM_VENDOR_RSVD, "VENDOR RESERVED" },
82 { LB_MEM_TABLE, "CONFIGURATION TABLES" },
83};
84
85static const char *bootmem_range_string(unsigned long tag)
86{
87 int i;
88
89 for (i = 0; i < ARRAY_SIZE(type_strings); i++) {
90 if (type_strings[i].tag == tag)
91 return type_strings[i].str;
92 }
93
94 return "UNKNOWN!";
95}
96
97void bootmem_dump_ranges(void)
98{
99 int i;
100 const struct range_entry *r;
101
102 i = 0;
103 memranges_each_entry(r, &bootmem) {
104 printk(BIOS_DEBUG, "%2d. %016llx-%016llx: %s\n",
105 i, range_entry_base(r), range_entry_end(r) - 1,
106 bootmem_range_string(range_entry_tag(r)));
107 i++;
108 }
109}
110
111int bootmem_region_targets_usable_ram(uint64_t start, uint64_t size)
112{
113 const struct range_entry *r;
114 uint64_t end = start + size;
115
116 memranges_each_entry(r, &bootmem) {
117 /* All further bootmem entries are beyond this range. */
118 if (end <= range_entry_base(r))
119 break;
120
121 if (start >= range_entry_base(r) && end <= range_entry_end(r)) {
122 if (range_entry_tag(r) == LB_MEM_RAM)
123 return 1;
124 }
125 }
126 return 0;
127}
128
129void *bootmem_allocate_buffer(size_t size)
130{
131 const struct range_entry *r;
132 const struct range_entry *region;
133 /* All allocated buffers fall below the 32-bit boundary. */
134 const resource_t max_addr = 1ULL << 32;
135 resource_t begin;
136 resource_t end;
137
138 /* 4KiB alignment. */
139 size = ALIGN(size, 4096);
140 region = NULL;
141 memranges_each_entry(r, &bootmem) {
142 if (range_entry_size(r) < size)
143 continue;
144
145 if (range_entry_tag(r) != LB_MEM_RAM)
146 continue;
147
148 if (range_entry_base(r) >= max_addr)
149 continue;
150
151 end = range_entry_end(r);
152 if (end > max_addr)
153 end = max_addr;
154
155 if ((end - range_entry_base(r)) < size)
156 continue;
157
158 region = r;
159 }
160
161 if (region == NULL)
162 return NULL;
163
164 /* region now points to the highest usable region for the given size. */
165 begin = range_entry_base(region);
166 end = range_entry_end(region);
167 if (end > max_addr)
168 end = max_addr;
169 begin = end - size;
170
171 /* Mark buffer as unusuable for future buffer use. */
172 bootmem_add_range(begin, size, LB_MEM_UNUSABLE);
173
174 return (void *)(uintptr_t)begin;
175}