blob: 5b7431479bbf7427b46387eb10ac2d86cfa8605a [file] [log] [blame]
Eric Biederman8ca8d762003-04-22 19:02:15 +00001#include <console/console.h>
2#include <mem.h>
3#include <ip_checksum.h>
4#include <boot/linuxbios_tables.h>
5#include "linuxbios_table.h"
6#include <string.h>
7#include <version.h>
Eric Biedermanb78c1972004-10-14 20:54:17 +00008#include <device/device.h>
9#include <stdlib.h>
Eric Biederman8ca8d762003-04-22 19:02:15 +000010
11
12struct lb_header *lb_table_init(unsigned long addr)
13{
14 struct lb_header *header;
15
16 /* 16 byte align the address */
17 addr += 15;
18 addr &= ~15;
19
20 header = (void *)addr;
21 header->signature[0] = 'L';
22 header->signature[1] = 'B';
23 header->signature[2] = 'I';
24 header->signature[3] = 'O';
25 header->header_bytes = sizeof(*header);
26 header->header_checksum = 0;
27 header->table_bytes = 0;
28 header->table_checksum = 0;
29 header->table_entries = 0;
30 return header;
31}
32
33struct lb_record *lb_first_record(struct lb_header *header)
34{
35 struct lb_record *rec;
36 rec = (void *)(((char *)header) + sizeof(*header));
37 return rec;
38}
39
40struct lb_record *lb_last_record(struct lb_header *header)
41{
42 struct lb_record *rec;
43 rec = (void *)(((char *)header) + sizeof(*header) + header->table_bytes);
44 return rec;
45}
46
47struct lb_record *lb_next_record(struct lb_record *rec)
48{
49 rec = (void *)(((char *)rec) + rec->size);
50 return rec;
51}
52
53struct lb_record *lb_new_record(struct lb_header *header)
54{
55 struct lb_record *rec;
56 rec = lb_last_record(header);
57 if (header->table_entries) {
58 header->table_bytes += rec->size;
59 }
60 rec = lb_last_record(header);
61 header->table_entries++;
62 rec->tag = LB_TAG_UNUSED;
63 rec->size = sizeof(*rec);
64 return rec;
65}
66
67
68struct lb_memory *lb_memory(struct lb_header *header)
69{
70 struct lb_record *rec;
71 struct lb_memory *mem;
72 rec = lb_new_record(header);
73 mem = (struct lb_memory *)rec;
74 mem->tag = LB_TAG_MEMORY;
75 mem->size = sizeof(*mem);
76 return mem;
77}
78
79struct lb_mainboard *lb_mainboard(struct lb_header *header)
80{
81 struct lb_record *rec;
82 struct lb_mainboard *mainboard;
83 rec = lb_new_record(header);
84 mainboard = (struct lb_mainboard *)rec;
85 mainboard->tag = LB_TAG_MAINBOARD;
86
87 mainboard->size = (sizeof(*mainboard) +
88 strlen(mainboard_vendor) + 1 +
89 strlen(mainboard_part_number) + 1 +
90 3) & ~3;
91
92 mainboard->vendor_idx = 0;
93 mainboard->part_number_idx = strlen(mainboard_vendor) + 1;
94
95 memcpy(mainboard->strings + mainboard->vendor_idx,
96 mainboard_vendor, strlen(mainboard_vendor) + 1);
97 memcpy(mainboard->strings + mainboard->part_number_idx,
98 mainboard_part_number, strlen(mainboard_part_number) + 1);
99
100 return mainboard;
101}
102
103void lb_strings(struct lb_header *header)
104{
105 static const struct {
106 uint32_t tag;
107 const uint8_t *string;
108 } strings[] = {
109 { LB_TAG_VERSION, linuxbios_version, },
110 { LB_TAG_EXTRA_VERSION, linuxbios_extra_version, },
111 { LB_TAG_BUILD, linuxbios_build, },
112 { LB_TAG_COMPILE_TIME, linuxbios_compile_time, },
113 { LB_TAG_COMPILE_BY, linuxbios_compile_by, },
114 { LB_TAG_COMPILE_HOST, linuxbios_compile_host, },
115 { LB_TAG_COMPILE_DOMAIN, linuxbios_compile_domain, },
116 { LB_TAG_COMPILER, linuxbios_compiler, },
117 { LB_TAG_LINKER, linuxbios_linker, },
118 { LB_TAG_ASSEMBLER, linuxbios_assembler, },
119 };
Eric Biederman52685572003-05-19 19:16:21 +0000120 unsigned int i;
Eric Biederman8ca8d762003-04-22 19:02:15 +0000121 for(i = 0; i < sizeof(strings)/sizeof(strings[0]); i++) {
122 struct lb_string *rec;
123 size_t len;
124 rec = (struct lb_string *)lb_new_record(header);
125 len = strlen(strings[i].string);
126 rec->tag = strings[i].tag;
127 rec->size = (sizeof(*rec) + len + 1 + 3) & ~3;
128 memcpy(rec->string, strings[i].string, len+1);
129 }
130
131}
132
133/* Some version of gcc have problems with 64 bit types so
134 * take an unsigned long instead of a uint64_t for now.
135 */
136void lb_memory_range(struct lb_memory *mem,
137 uint32_t type, unsigned long start, unsigned long size)
138{
139 int entries;
140 entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]);
141 mem->map[entries].start = start;
142 mem->map[entries].size = size;
143 mem->map[entries].type = type;
144 mem->size += sizeof(mem->map[0]);
145}
146
147static void lb_memory_rangek(struct lb_memory *mem,
148 uint32_t type, unsigned long startk, unsigned long endk)
149{
150 int entries;
151 entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]);
152 mem->map[entries].start = startk;
153 mem->map[entries].start <<= 10;
154 mem->map[entries].size = endk - startk;
155 mem->map[entries].size <<= 10;
156 mem->map[entries].type = type;
157 mem->size += sizeof(mem->map[0]);
158}
159
160static void lb_reserve_table_memory(struct lb_header *head)
161{
162 struct lb_record *last_rec;
163 struct lb_memory *mem;
164 uint64_t start;
165 uint64_t end;
166 int i, entries;
167
168 last_rec = lb_last_record(head);
169 mem = get_lb_mem();
170 start = (unsigned long)head;
171 end = (unsigned long)last_rec;
172 entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]);
173 /* Resize the right two memory areas so this table is in
174 * a reserved area of memory. Everything has been carefully
175 * setup so that is all we need to do.
176 */
177 for(i = 0; i < entries; i++ ) {
178 uint64_t map_start = mem->map[i].start;
179 uint64_t map_end = map_start + mem->map[i].size;
180 /* Does this area need to be expanded? */
181 if (map_end == start) {
182 mem->map[i].size = end - map_start;
183 }
184 /* Does this area need to be contracted? */
185 else if (map_start == start) {
186 mem->map[i].start = end;
187 mem->map[i].size = map_end - end;
188 }
189 }
190}
191
192
193unsigned long lb_table_fini(struct lb_header *head)
194{
195 struct lb_record *rec, *first_rec;
196 rec = lb_last_record(head);
197 if (head->table_entries) {
198 head->table_bytes += rec->size;
199 }
200 lb_reserve_table_memory(head);
201 first_rec = lb_first_record(head);
202 head->table_checksum = compute_ip_checksum(first_rec, head->table_bytes);
203 head->header_checksum = 0;
204 head->header_checksum = compute_ip_checksum(head, sizeof(*head));
205 printk_debug("Wrote linuxbios table at: %p - %p checksum %lx\n",
206 head, rec, head->table_checksum);
207 return (unsigned long)rec;
208}
209
210
211/* Routines to extract part so the linuxBIOS table or
212 * information from the linuxBIOS table after we have written it.
213 * Currently get_lb_mem relies on a global we can change the
214 * implementaiton.
215 */
216static struct lb_memory *mem_ranges = 0;
217struct lb_memory *get_lb_mem(void)
218{
219 return mem_ranges;
220}
221
Eric Biedermanb78c1972004-10-14 20:54:17 +0000222struct mem_range *sizeram(void)
223{
224 struct mem_range *mem, *rmem;
225 struct device *dev;
226 unsigned int count;
227 count = 0;
228 for(dev = all_devices; dev; dev = dev->next) {
229 struct resource *res, *last;
230 last = &dev->resource[dev->resources];
231 for(res = &dev->resource[0]; res < last; res++) {
232 if ((res->flags & IORESOURCE_MEM) &&
233 (res->flags & IORESOURCE_CACHEABLE))
234 {
235 count++;
236 }
237 }
238 }
239 rmem = mem = malloc(sizeof(*mem) * (count + 1));
240 for(dev = all_devices; dev; dev = dev->next) {
241 struct resource *res, *last;
242 last = &dev->resource[dev->resources];
243 for(res = &dev->resource[0]; res < last; res++) {
244 if ((res->flags & IORESOURCE_MEM) &&
245 (res->flags & IORESOURCE_CACHEABLE))
246 {
247 mem->basek = res->base >> 10;
248 mem->sizek = res->size >> 10;
249 mem++;
250 }
251 }
252 }
253 mem->basek = 0;
254 mem->sizek = 0;
255#if 0
256 for(mem = rmem; mem->sizek; mem++) {
257 printk_debug("basek: %lu sizek: %lu\n",
258 mem->basek, mem->sizek);
259 }
260#endif
261 return rmem;
262}
263
264static struct mem_range *get_ramsize(void)
265{
266 struct mem_range *mem = 0;
267 if (!mem) {
268 mem = sizeram();
269 }
270 if (!mem) {
271 printk_emerg("No memory size information!\n");
272 for(;;) {
273 /* Ensure this loop is not optimized away */
274 asm volatile("":/* outputs */:/*inputs */ :"memory");
275 }
276 }
277 return mem;
278}
279
Eric Biederman8ca8d762003-04-22 19:02:15 +0000280unsigned long write_linuxbios_table(
Eric Biederman8ca8d762003-04-22 19:02:15 +0000281 unsigned long low_table_start, unsigned long low_table_end,
282 unsigned long rom_table_startk, unsigned long rom_table_endk)
283{
284 unsigned long table_size;
Eric Biedermanb78c1972004-10-14 20:54:17 +0000285 struct mem_range *ram, *ramp;
Eric Biederman8ca8d762003-04-22 19:02:15 +0000286 struct lb_header *head;
287 struct lb_memory *mem;
288 struct lb_record *rec_dest, *rec_src;
289
Eric Biedermanb78c1972004-10-14 20:54:17 +0000290 ram = get_ramsize();
Eric Biederman8ca8d762003-04-22 19:02:15 +0000291 head = lb_table_init(low_table_end);
292 low_table_end = (unsigned long)head;
293#if HAVE_OPTION_TABLE == 1
294 /* Write the option config table... */
295 rec_dest = lb_new_record(head);
296 rec_src = (struct lb_record *)&option_table;
297 memcpy(rec_dest, rec_src, rec_src->size);
298#endif
299 mem = lb_memory(head);
300 mem_ranges = mem;
301 /* I assume there is always ram at address 0 */
302 /* Reserve our tables in low memory */
303 table_size = (low_table_end - low_table_start);
304 lb_memory_range(mem, LB_MEM_TABLE, 0, table_size);
305 lb_memory_range(mem, LB_MEM_RAM, table_size, (ram[0].sizek << 10) - table_size);
306 /* Reserving pci memory mapped space will keep the kernel from booting seeing
307 * any pci resources.
308 */
309 for(ramp = &ram[1]; ramp->sizek; ramp++) {
310 unsigned long startk, endk;
311 startk = ramp->basek;
312 endk = startk + ramp->sizek;
313 if ((startk < rom_table_startk) && (endk > rom_table_startk)) {
314 lb_memory_rangek(mem, LB_MEM_RAM, startk, rom_table_startk);
315 startk = rom_table_startk;
316 }
317 if ((startk == rom_table_startk) && (endk > startk)) {
318 unsigned long tend;
319 tend = rom_table_endk;
320 if (tend > endk) {
321 tend = endk;
322 }
323 lb_memory_rangek(mem, LB_MEM_TABLE, rom_table_startk, tend);
324 startk = tend;
325 }
326 if (endk > startk) {
327 lb_memory_rangek(mem, LB_MEM_RAM, startk, endk);
328 }
329 }
330
331 /* Record our motheboard */
332 lb_mainboard(head);
333 /* Record our various random string information */
334 lb_strings(head);
335
336 low_table_end = lb_table_fini(head);
337
338 /* Remember where my valid memory ranges are */
339 return low_table_end;
340}