1 // Support for manipulating bios tables (pir, mptable, acpi, smbios).
2 //
3 // Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net>
4 //
5 // This file may be distributed under the terms of the GNU LGPLv3 license.
6
7 #include "byteorder.h" // le32_to_cpu
8 #include "config.h" // CONFIG_*
9 #include "hw/pci.h" // pci_config_writeb
10 #include "malloc.h" // malloc_fseg
11 #include "memmap.h" // SYMBOL
12 #include "output.h" // dprintf
13 #include "romfile.h" // romfile_find
14 #include "std/acpi.h" // struct rsdp_descriptor
15 #include "std/mptable.h" // MPTABLE_SIGNATURE
16 #include "std/pirtable.h" // struct pir_header
17 #include "std/smbios.h" // struct smbios_entry_point
18 #include "string.h" // memcpy
19 #include "util.h" // copy_table
20 #include "x86.h" // outb
21
22 struct pir_header *PirAddr VARFSEG;
23
24 void
copy_pir(void * pos)25 copy_pir(void *pos)
26 {
27 struct pir_header *p = pos;
28 if (p->signature != PIR_SIGNATURE)
29 return;
30 if (PirAddr)
31 return;
32 if (p->size < sizeof(*p))
33 return;
34 if (checksum(pos, p->size) != 0)
35 return;
36 void *newpos = malloc_fseg(p->size);
37 if (!newpos) {
38 warn_noalloc();
39 return;
40 }
41 dprintf(1, "Copying PIR from %p to %p\n", pos, newpos);
42 memcpy(newpos, pos, p->size);
43 PirAddr = newpos;
44 }
45
46 void
copy_mptable(void * pos)47 copy_mptable(void *pos)
48 {
49 struct mptable_floating_s *p = pos;
50 if (p->signature != MPTABLE_SIGNATURE)
51 return;
52 if (!p->physaddr)
53 return;
54 if (checksum(pos, sizeof(*p)) != 0)
55 return;
56 u32 length = p->length * 16;
57 u16 mpclength = ((struct mptable_config_s *)p->physaddr)->length;
58 if (length + mpclength > BUILD_MAX_MPTABLE_FSEG) {
59 dprintf(1, "Skipping MPTABLE copy due to large size (%d bytes)\n"
60 , length + mpclength);
61 return;
62 }
63 // Allocate final memory location. (In theory the config
64 // structure can go in high memory, but Linux kernels before
65 // v2.6.30 crash with that.)
66 struct mptable_floating_s *newpos = malloc_fseg(length + mpclength);
67 if (!newpos) {
68 warn_noalloc();
69 return;
70 }
71 dprintf(1, "Copying MPTABLE from %p/%x to %p\n", pos, p->physaddr, newpos);
72 memcpy(newpos, pos, length);
73 newpos->physaddr = (u32)newpos + length;
74 newpos->checksum -= checksum(newpos, sizeof(*newpos));
75 memcpy((void*)newpos + length, (void*)p->physaddr, mpclength);
76 }
77
78
79 /****************************************************************
80 * ACPI
81 ****************************************************************/
82
83 static int
get_acpi_rsdp_length(void * pos,unsigned size)84 get_acpi_rsdp_length(void *pos, unsigned size)
85 {
86 struct rsdp_descriptor *p = pos;
87 if (p->signature != RSDP_SIGNATURE)
88 return -1;
89 u32 length = 20;
90 if (length > size)
91 return -1;
92 if (checksum(pos, length) != 0)
93 return -1;
94 if (p->revision > 1) {
95 length = p->length;
96 if (length > size)
97 return -1;
98 if (checksum(pos, length) != 0)
99 return -1;
100 }
101 return length;
102 }
103
104 struct rsdp_descriptor *RsdpAddr;
105
106 void
copy_acpi_rsdp(void * pos)107 copy_acpi_rsdp(void *pos)
108 {
109 if (RsdpAddr)
110 return;
111 int length = get_acpi_rsdp_length(pos, -1);
112 if (length < 0)
113 return;
114 void *newpos = malloc_fseg(length);
115 if (!newpos) {
116 warn_noalloc();
117 return;
118 }
119 dprintf(1, "Copying ACPI RSDP from %p to %p\n", pos, newpos);
120 memcpy(newpos, pos, length);
121 RsdpAddr = newpos;
122 }
123
find_acpi_rsdp(void)124 void *find_acpi_rsdp(void)
125 {
126 unsigned long start = SYMBOL(zonefseg_start);
127 unsigned long end = SYMBOL(zonefseg_end);
128 unsigned long pos;
129
130 for (pos = ALIGN(start, 0x10); pos <= ALIGN_DOWN(end, 0x10); pos += 0x10)
131 if (get_acpi_rsdp_length((void *)pos, end - pos) >= 0)
132 return (void *)pos;
133
134 return NULL;
135 }
136
137 void *
find_acpi_table(u32 signature)138 find_acpi_table(u32 signature)
139 {
140 dprintf(4, "rsdp=%p\n", RsdpAddr);
141 if (!RsdpAddr || RsdpAddr->signature != RSDP_SIGNATURE)
142 return NULL;
143 struct rsdt_descriptor_rev1 *rsdt = (void*)RsdpAddr->rsdt_physical_address;
144 dprintf(4, "rsdt=%p\n", rsdt);
145 if (!rsdt || rsdt->signature != RSDT_SIGNATURE)
146 return NULL;
147 void *end = (void*)rsdt + rsdt->length;
148 int i;
149 for (i=0; (void*)&rsdt->table_offset_entry[i] < end; i++) {
150 struct acpi_table_header *tbl = (void*)rsdt->table_offset_entry[i];
151 if (!tbl || tbl->signature != signature)
152 continue;
153 dprintf(4, "table(%x)=%p\n", signature, tbl);
154 return tbl;
155 }
156 dprintf(4, "no table %x found\n", signature);
157 return NULL;
158 }
159
160 u32
find_resume_vector(void)161 find_resume_vector(void)
162 {
163 struct fadt_descriptor_rev1 *fadt = find_acpi_table(FACP_SIGNATURE);
164 if (!fadt)
165 return 0;
166 struct facs_descriptor_rev1 *facs = (void*)fadt->firmware_ctrl;
167 dprintf(4, "facs=%p\n", facs);
168 if (! facs || facs->signature != FACS_SIGNATURE)
169 return 0;
170 // Found it.
171 dprintf(4, "resume addr=%d\n", facs->firmware_waking_vector);
172 return facs->firmware_waking_vector;
173 }
174
175 static struct acpi_20_generic_address acpi_reset_reg;
176 static u8 acpi_reset_val;
177 u32 acpi_pm1a_cnt VARFSEG;
178 u16 acpi_pm_base = 0xb000;
179
180 #define acpi_ga_to_bdf(addr) pci_to_bdf(0, (addr >> 32) & 0xffff, (addr >> 16) & 0xffff)
181
182 void
acpi_reboot(void)183 acpi_reboot(void)
184 {
185 // Check it passed the sanity checks in acpi_set_reset_reg() and was set
186 if (acpi_reset_reg.register_bit_width != 8)
187 return;
188
189 u64 addr = le64_to_cpu(acpi_reset_reg.address);
190
191 dprintf(1, "ACPI hard reset %d:%llx (%x)\n",
192 acpi_reset_reg.address_space_id, addr, acpi_reset_val);
193
194 switch (acpi_reset_reg.address_space_id) {
195 case 0: // System Memory
196 writeb((void *)(u32)addr, acpi_reset_val);
197 break;
198 case 1: // System I/O
199 outb(acpi_reset_val, addr);
200 break;
201 case 2: // PCI config space
202 pci_config_writeb(acpi_ga_to_bdf(addr), addr & 0xffff, acpi_reset_val);
203 break;
204 }
205 }
206
207 static void
acpi_set_reset_reg(struct acpi_20_generic_address * reg,u8 val)208 acpi_set_reset_reg(struct acpi_20_generic_address *reg, u8 val)
209 {
210 if (!reg || reg->address_space_id > 2 ||
211 reg->register_bit_width != 8 || reg->register_bit_offset)
212 return;
213
214 acpi_reset_reg = *reg;
215 acpi_reset_val = val;
216 }
217
218 void
find_acpi_features(void)219 find_acpi_features(void)
220 {
221 struct fadt_descriptor_rev1 *fadt = find_acpi_table(FACP_SIGNATURE);
222 if (!fadt)
223 return;
224 u32 pm_tmr = le32_to_cpu(fadt->pm_tmr_blk);
225 u32 pm1a_cnt = le32_to_cpu(fadt->pm1a_cnt_blk);
226 dprintf(4, "pm_tmr_blk=%x\n", pm_tmr);
227 if (pm_tmr)
228 pmtimer_setup(pm_tmr);
229 if (pm1a_cnt)
230 acpi_pm1a_cnt = pm1a_cnt;
231
232 // Theoretically we should check the 'reset_reg_sup' flag, but Windows
233 // doesn't and thus nobody seems to *set* it. If the table is large enough
234 // to include it, let the sanity checks in acpi_set_reset_reg() suffice.
235 if (fadt->length >= 129) {
236 void *p = fadt;
237 acpi_set_reset_reg(p + 116, *(u8 *)(p + 128));
238 }
239 }
240
241
242 /****************************************************************
243 * SMBIOS
244 ****************************************************************/
245
246 // Iterator for each sub-table in the smbios blob.
247 void *
smbios_next(struct smbios_entry_point * smbios,void * prev)248 smbios_next(struct smbios_entry_point *smbios, void *prev)
249 {
250 if (!smbios)
251 return NULL;
252 void *start = (void*)smbios->structure_table_address;
253 void *end = start + smbios->structure_table_length;
254
255 if (!prev) {
256 prev = start;
257 } else {
258 struct smbios_structure_header *hdr = prev;
259 if (prev + sizeof(*hdr) > end)
260 return NULL;
261 prev += hdr->length + 2;
262 while (prev < end && (*(u8*)(prev-1) != '\0' || *(u8*)(prev-2) != '\0'))
263 prev++;
264 }
265 struct smbios_structure_header *hdr = prev;
266 if (prev >= end || prev + sizeof(*hdr) >= end || prev + hdr->length >= end)
267 return NULL;
268 return prev;
269 }
270
271 struct smbios_entry_point *SMBiosAddr;
272
273 void
copy_smbios(void * pos)274 copy_smbios(void *pos)
275 {
276 if (SMBiosAddr)
277 return;
278 struct smbios_entry_point *p = pos;
279 if (p->signature != SMBIOS_SIGNATURE)
280 return;
281 if (checksum(pos, 0x10) != 0)
282 return;
283 if (memcmp(p->intermediate_anchor_string, "_DMI_", 5))
284 return;
285 if (checksum(pos+0x10, p->length-0x10) != 0)
286 return;
287 struct smbios_entry_point *newpos = malloc_fseg(p->length);
288 if (!newpos) {
289 warn_noalloc();
290 return;
291 }
292 dprintf(1, "Copying SMBIOS entry point from %p to %p\n", pos, newpos);
293 memcpy(newpos, pos, p->length);
294 SMBiosAddr = newpos;
295 }
296
297 void
display_uuid(void)298 display_uuid(void)
299 {
300 struct smbios_type_1 *tbl = smbios_next(SMBiosAddr, NULL);
301 int minlen = offsetof(struct smbios_type_1, uuid) + sizeof(tbl->uuid);
302 for (; tbl; tbl = smbios_next(SMBiosAddr, tbl))
303 if (tbl->header.type == 1 && tbl->header.length >= minlen) {
304 u8 *uuid = tbl->uuid;
305 u8 empty_uuid[sizeof(tbl->uuid)] = { 0 };
306 if (memcmp(uuid, empty_uuid, sizeof(empty_uuid)) == 0)
307 return;
308
309 /*
310 * According to SMBIOS v2.6 the first three fields are encoded in
311 * little-endian format. Versions prior to v2.6 did not specify
312 * the encoding, but we follow dmidecode and assume big-endian
313 * encoding.
314 */
315 if (SMBiosAddr->smbios_major_version > 2 ||
316 (SMBiosAddr->smbios_major_version == 2 &&
317 SMBiosAddr->smbios_minor_version >= 6)) {
318 printf("Machine UUID"
319 " %02x%02x%02x%02x"
320 "-%02x%02x"
321 "-%02x%02x"
322 "-%02x%02x"
323 "-%02x%02x%02x%02x%02x%02x\n"
324 , uuid[ 3], uuid[ 2], uuid[ 1], uuid[ 0]
325 , uuid[ 5], uuid[ 4]
326 , uuid[ 7], uuid[ 6]
327 , uuid[ 8], uuid[ 9]
328 , uuid[10], uuid[11], uuid[12]
329 , uuid[13], uuid[14], uuid[15]);
330 } else {
331 printf("Machine UUID"
332 " %02x%02x%02x%02x"
333 "-%02x%02x"
334 "-%02x%02x"
335 "-%02x%02x"
336 "-%02x%02x%02x%02x%02x%02x\n"
337 , uuid[ 0], uuid[ 1], uuid[ 2], uuid[ 3]
338 , uuid[ 4], uuid[ 5]
339 , uuid[ 6], uuid[ 7]
340 , uuid[ 8], uuid[ 9]
341 , uuid[10], uuid[11], uuid[12]
342 , uuid[13], uuid[14], uuid[15]);
343 }
344
345 return;
346 }
347 }
348
349 #define set_str_field_or_skip(type, field, value) \
350 do { \
351 int size = (value != NULL) ? strlen(value) + 1 : 0; \
352 if (size > 1) { \
353 memcpy(end, value, size); \
354 end += size; \
355 p->field = ++str_index; \
356 } else { \
357 p->field = 0; \
358 } \
359 } while (0)
360
361 static void *
smbios_new_type_0(void * start,const char * vendor,const char * version,const char * date)362 smbios_new_type_0(void *start,
363 const char *vendor, const char *version, const char *date)
364 {
365 struct smbios_type_0 *p = (struct smbios_type_0 *)start;
366 char *end = (char *)start + sizeof(struct smbios_type_0);
367 int str_index = 0;
368
369 p->header.type = 0;
370 p->header.length = sizeof(struct smbios_type_0);
371 p->header.handle = 0;
372
373 set_str_field_or_skip(0, vendor_str, vendor);
374 set_str_field_or_skip(0, bios_version_str, version);
375 p->bios_starting_address_segment = 0xe800;
376 set_str_field_or_skip(0, bios_release_date_str, date);
377
378 p->bios_rom_size = 0; /* FIXME */
379
380 /* BIOS characteristics not supported */
381 memset(p->bios_characteristics, 0, 8);
382 p->bios_characteristics[0] = 0x08;
383
384 /* Enable targeted content distribution (needed for SVVP) */
385 p->bios_characteristics_extension_bytes[0] = 0;
386 p->bios_characteristics_extension_bytes[1] = 4;
387
388 p->system_bios_major_release = 0;
389 p->system_bios_minor_release = 0;
390 p->embedded_controller_major_release = 0xFF;
391 p->embedded_controller_minor_release = 0xFF;
392
393 *end = 0;
394 end++;
395 if (!str_index) {
396 *end = 0;
397 end++;
398 }
399
400 return end;
401 }
402
403 #define BIOS_NAME "SeaBIOS"
404 #define BIOS_DATE "04/01/2014"
405
406 static int
smbios_romfile_setup(void)407 smbios_romfile_setup(void)
408 {
409 struct romfile_s *f_anchor = romfile_find("etc/smbios/smbios-anchor");
410 struct romfile_s *f_tables = romfile_find("etc/smbios/smbios-tables");
411 struct smbios_entry_point ep;
412 struct smbios_type_0 *t0;
413 u16 qtables_len, need_t0 = 1;
414 u8 *qtables, *tables;
415
416 if (!f_anchor || !f_tables || f_anchor->size != sizeof(ep))
417 return 0;
418
419 f_anchor->copy(f_anchor, &ep, f_anchor->size);
420
421 if (f_tables->size != ep.structure_table_length)
422 return 0;
423
424 qtables = malloc_tmphigh(f_tables->size);
425 if (!qtables) {
426 warn_noalloc();
427 return 0;
428 }
429 f_tables->copy(f_tables, qtables, f_tables->size);
430 ep.structure_table_address = (u32)qtables; /* for smbios_next(), below */
431
432 /* did we get a type 0 structure ? */
433 for (t0 = smbios_next(&ep, NULL); t0; t0 = smbios_next(&ep, t0))
434 if (t0->header.type == 0) {
435 need_t0 = 0;
436 break;
437 }
438
439 qtables_len = ep.structure_table_length;
440 if (need_t0) {
441 /* common case: add our own type 0, with 3 strings and 4 '\0's */
442 u16 t0_len = sizeof(struct smbios_type_0) + strlen(BIOS_NAME) +
443 strlen(VERSION) + strlen(BIOS_DATE) + 4;
444 ep.structure_table_length += t0_len;
445 if (t0_len > ep.max_structure_size)
446 ep.max_structure_size = t0_len;
447 ep.number_of_structures++;
448 }
449
450 /* allocate final blob and record its address in the entry point */
451 if (ep.structure_table_length > BUILD_MAX_SMBIOS_FSEG)
452 tables = malloc_high(ep.structure_table_length);
453 else
454 tables = malloc_fseg(ep.structure_table_length);
455 if (!tables) {
456 warn_noalloc();
457 free(qtables);
458 return 0;
459 }
460 ep.structure_table_address = (u32)tables;
461
462 /* populate final blob */
463 if (need_t0)
464 tables = smbios_new_type_0(tables, BIOS_NAME, VERSION, BIOS_DATE);
465 memcpy(tables, qtables, qtables_len);
466 free(qtables);
467
468 /* finalize entry point */
469 ep.checksum -= checksum(&ep, 0x10);
470 ep.intermediate_checksum -= checksum((void *)&ep + 0x10, ep.length - 0x10);
471
472 copy_smbios(&ep);
473 return 1;
474 }
475
476 void
smbios_setup(void)477 smbios_setup(void)
478 {
479 if (smbios_romfile_setup())
480 return;
481 smbios_legacy_setup();
482 }
483
484 void
copy_table(void * pos)485 copy_table(void *pos)
486 {
487 copy_pir(pos);
488 copy_mptable(pos);
489 copy_acpi_rsdp(pos);
490 copy_smbios(pos);
491 }
492