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     struct xsdt_descriptor_rev2 *xsdt =
145         RsdpAddr->xsdt_physical_address >= 0x100000000
146         ? NULL : (void*)(u32)(RsdpAddr->xsdt_physical_address);
147     dprintf(4, "rsdt=%p\n", rsdt);
148     dprintf(4, "xsdt=%p\n", xsdt);
149 
150     if (xsdt && xsdt->signature == XSDT_SIGNATURE) {
151         void *end = (void*)xsdt + xsdt->length;
152         int i;
153         for (i=0; (void*)&xsdt->table_offset_entry[i] < end; i++) {
154             if (xsdt->table_offset_entry[i] >= 0x100000000)
155                 continue; /* above 4G */
156             struct acpi_table_header *tbl = (void*)(u32)xsdt->table_offset_entry[i];
157             if (!tbl || tbl->signature != signature)
158                 continue;
159             dprintf(1, "table(%x)=%p (via xsdt)\n", signature, tbl);
160             return tbl;
161         }
162     }
163 
164     if (rsdt && rsdt->signature == RSDT_SIGNATURE) {
165         void *end = (void*)rsdt + rsdt->length;
166         int i;
167         for (i=0; (void*)&rsdt->table_offset_entry[i] < end; i++) {
168             struct acpi_table_header *tbl = (void*)rsdt->table_offset_entry[i];
169             if (!tbl || tbl->signature != signature)
170                 continue;
171             dprintf(1, "table(%x)=%p (via rsdt)\n", signature, tbl);
172             return tbl;
173         }
174     }
175 
176     dprintf(4, "no table %x found\n", signature);
177     return NULL;
178 }
179 
180 u32
find_resume_vector(void)181 find_resume_vector(void)
182 {
183     struct fadt_descriptor_rev1 *fadt = find_acpi_table(FACP_SIGNATURE);
184     if (!fadt)
185         return 0;
186     struct facs_descriptor_rev1 *facs = (void*)fadt->firmware_ctrl;
187     dprintf(4, "facs=%p\n", facs);
188     if (! facs || facs->signature != FACS_SIGNATURE)
189         return 0;
190     // Found it.
191     dprintf(4, "resume addr=%d\n", facs->firmware_waking_vector);
192     return facs->firmware_waking_vector;
193 }
194 
195 static struct acpi_20_generic_address acpi_reset_reg;
196 static u8 acpi_reset_val;
197 u32 acpi_pm1a_cnt VARFSEG;
198 u16 acpi_pm_base = 0xb000;
199 
200 #define acpi_ga_to_bdf(addr) pci_to_bdf(0, (addr >> 32) & 0xffff, (addr >> 16) & 0xffff)
201 
202 void
acpi_reboot(void)203 acpi_reboot(void)
204 {
205     // Check it passed the sanity checks in acpi_set_reset_reg() and was set
206     if (acpi_reset_reg.register_bit_width != 8)
207         return;
208 
209     u64 addr = le64_to_cpu(acpi_reset_reg.address);
210 
211     dprintf(1, "ACPI hard reset %d:%llx (%x)\n",
212             acpi_reset_reg.address_space_id, addr, acpi_reset_val);
213 
214     switch (acpi_reset_reg.address_space_id) {
215     case 0: // System Memory
216         writeb((void *)(u32)addr, acpi_reset_val);
217         break;
218     case 1: // System I/O
219         outb(acpi_reset_val, addr);
220         break;
221     case 2: // PCI config space
222         pci_config_writeb(acpi_ga_to_bdf(addr), addr & 0xffff, acpi_reset_val);
223         break;
224     }
225 }
226 
227 static void
acpi_set_reset_reg(struct acpi_20_generic_address * reg,u8 val)228 acpi_set_reset_reg(struct acpi_20_generic_address *reg, u8 val)
229 {
230     if (!reg || reg->address_space_id > 2 ||
231         reg->register_bit_width != 8 || reg->register_bit_offset)
232         return;
233 
234     acpi_reset_reg = *reg;
235     acpi_reset_val = val;
236 }
237 
238 void
find_acpi_features(void)239 find_acpi_features(void)
240 {
241     struct fadt_descriptor_rev1 *fadt = find_acpi_table(FACP_SIGNATURE);
242     if (!fadt)
243         return;
244     u32 pm_tmr = le32_to_cpu(fadt->pm_tmr_blk);
245     u32 pm1a_cnt = le32_to_cpu(fadt->pm1a_cnt_blk);
246     dprintf(4, "pm_tmr_blk=%x\n", pm_tmr);
247     if (pm_tmr)
248         pmtimer_setup(pm_tmr);
249     if (pm1a_cnt)
250         acpi_pm1a_cnt = pm1a_cnt;
251 
252     // Theoretically we should check the 'reset_reg_sup' flag, but Windows
253     // doesn't and thus nobody seems to *set* it. If the table is large enough
254     // to include it, let the sanity checks in acpi_set_reset_reg() suffice.
255     if (fadt->length >= 129) {
256         void *p = fadt;
257         acpi_set_reset_reg(p + 116, *(u8 *)(p + 128));
258     }
259     acpi_dsdt_parse();
260 }
261 
262 
263 /****************************************************************
264  * SMBIOS
265  ****************************************************************/
266 
267 // Iterator for each sub-table in the smbios blob.
268 void *
smbios_next(struct smbios_entry_point * smbios,void * prev)269 smbios_next(struct smbios_entry_point *smbios, void *prev)
270 {
271     if (!smbios)
272         return NULL;
273     void *start = (void*)smbios->structure_table_address;
274     void *end = start + smbios->structure_table_length;
275 
276     if (!prev) {
277         prev = start;
278     } else {
279         struct smbios_structure_header *hdr = prev;
280         if (prev + sizeof(*hdr) > end)
281             return NULL;
282         prev += hdr->length + 2;
283         while (prev < end && (*(u8*)(prev-1) != '\0' || *(u8*)(prev-2) != '\0'))
284             prev++;
285     }
286     struct smbios_structure_header *hdr = prev;
287     if (prev >= end || prev + sizeof(*hdr) >= end || prev + hdr->length >= end)
288         return NULL;
289     return prev;
290 }
291 
292 struct smbios_entry_point *SMBiosAddr;
293 
294 void
copy_smbios(void * pos)295 copy_smbios(void *pos)
296 {
297     if (SMBiosAddr)
298         return;
299     struct smbios_entry_point *p = pos;
300     if (p->signature != SMBIOS_SIGNATURE)
301         return;
302     if (checksum(pos, 0x10) != 0)
303         return;
304     if (memcmp(p->intermediate_anchor_string, "_DMI_", 5))
305         return;
306     if (checksum(pos+0x10, p->length-0x10) != 0)
307         return;
308     struct smbios_entry_point *newpos = malloc_fseg(p->length);
309     if (!newpos) {
310         warn_noalloc();
311         return;
312     }
313     dprintf(1, "Copying SMBIOS entry point from %p to %p\n", pos, newpos);
314     memcpy(newpos, pos, p->length);
315     SMBiosAddr = newpos;
316 }
317 
318 void
display_uuid(void)319 display_uuid(void)
320 {
321     struct smbios_type_1 *tbl = smbios_next(SMBiosAddr, NULL);
322     int minlen = offsetof(struct smbios_type_1, uuid) + sizeof(tbl->uuid);
323     for (; tbl; tbl = smbios_next(SMBiosAddr, tbl))
324         if (tbl->header.type == 1 && tbl->header.length >= minlen) {
325             u8 *uuid = tbl->uuid;
326             u8 empty_uuid[sizeof(tbl->uuid)] = { 0 };
327             if (memcmp(uuid, empty_uuid, sizeof(empty_uuid)) == 0)
328                 return;
329 
330             /*
331              * According to SMBIOS v2.6 the first three fields are encoded in
332              * little-endian format.  Versions prior to v2.6 did not specify
333              * the encoding, but we follow dmidecode and assume big-endian
334              * encoding.
335              */
336             if (SMBiosAddr->smbios_major_version > 2 ||
337                 (SMBiosAddr->smbios_major_version == 2 &&
338                  SMBiosAddr->smbios_minor_version >= 6)) {
339                 printf("Machine UUID"
340                        " %02x%02x%02x%02x"
341                        "-%02x%02x"
342                        "-%02x%02x"
343                        "-%02x%02x"
344                        "-%02x%02x%02x%02x%02x%02x\n"
345                        , uuid[ 3], uuid[ 2], uuid[ 1], uuid[ 0]
346                        , uuid[ 5], uuid[ 4]
347                        , uuid[ 7], uuid[ 6]
348                        , uuid[ 8], uuid[ 9]
349                        , uuid[10], uuid[11], uuid[12]
350                        , uuid[13], uuid[14], uuid[15]);
351             } else {
352                 printf("Machine UUID"
353                        " %02x%02x%02x%02x"
354                        "-%02x%02x"
355                        "-%02x%02x"
356                        "-%02x%02x"
357                        "-%02x%02x%02x%02x%02x%02x\n"
358                        , uuid[ 0], uuid[ 1], uuid[ 2], uuid[ 3]
359                        , uuid[ 4], uuid[ 5]
360                        , uuid[ 6], uuid[ 7]
361                        , uuid[ 8], uuid[ 9]
362                        , uuid[10], uuid[11], uuid[12]
363                        , uuid[13], uuid[14], uuid[15]);
364             }
365 
366             return;
367         }
368 }
369 
370 #define set_str_field_or_skip(type, field, value)                       \
371     do {                                                                \
372         int size = (value != NULL) ? strlen(value) + 1 : 0;             \
373         if (size > 1) {                                                 \
374             memcpy(end, value, size);                                   \
375             end += size;                                                \
376             p->field = ++str_index;                                     \
377         } else {                                                        \
378             p->field = 0;                                               \
379         }                                                               \
380     } while (0)
381 
382 static void *
smbios_new_type_0(void * start,const char * vendor,const char * version,const char * date)383 smbios_new_type_0(void *start,
384                   const char *vendor, const char *version, const char *date)
385 {
386     struct smbios_type_0 *p = (struct smbios_type_0 *)start;
387     char *end = (char *)start + sizeof(struct smbios_type_0);
388     int str_index = 0;
389 
390     p->header.type = 0;
391     p->header.length = sizeof(struct smbios_type_0);
392     p->header.handle = 0;
393 
394     set_str_field_or_skip(0, vendor_str, vendor);
395     set_str_field_or_skip(0, bios_version_str, version);
396     p->bios_starting_address_segment = 0xe800;
397     set_str_field_or_skip(0, bios_release_date_str, date);
398 
399     p->bios_rom_size = 0; /* FIXME */
400 
401     /* BIOS characteristics not supported */
402     memset(p->bios_characteristics, 0, 8);
403     p->bios_characteristics[0] = 0x08;
404 
405     /* Enable targeted content distribution (needed for SVVP) */
406     p->bios_characteristics_extension_bytes[0] = 0;
407     p->bios_characteristics_extension_bytes[1] = 4;
408 
409     p->system_bios_major_release = 0;
410     p->system_bios_minor_release = 0;
411     p->embedded_controller_major_release = 0xFF;
412     p->embedded_controller_minor_release = 0xFF;
413 
414     *end = 0;
415     end++;
416     if (!str_index) {
417         *end = 0;
418         end++;
419     }
420 
421     return end;
422 }
423 
424 #define BIOS_NAME "SeaBIOS"
425 #define BIOS_DATE "04/01/2014"
426 
427 static int
smbios_romfile_setup(void)428 smbios_romfile_setup(void)
429 {
430     struct romfile_s *f_anchor = romfile_find("etc/smbios/smbios-anchor");
431     struct romfile_s *f_tables = romfile_find("etc/smbios/smbios-tables");
432     struct smbios_entry_point ep;
433     struct smbios_type_0 *t0;
434     u16 qtables_len, need_t0 = 1;
435     u8 *qtables, *tables;
436 
437     if (!f_anchor || !f_tables || f_anchor->size != sizeof(ep))
438         return 0;
439 
440     f_anchor->copy(f_anchor, &ep, f_anchor->size);
441 
442     if (f_tables->size != ep.structure_table_length)
443         return 0;
444 
445     qtables = malloc_tmphigh(f_tables->size);
446     if (!qtables) {
447         warn_noalloc();
448         return 0;
449     }
450     f_tables->copy(f_tables, qtables, f_tables->size);
451     ep.structure_table_address = (u32)qtables; /* for smbios_next(), below */
452 
453     /* did we get a type 0 structure ? */
454     for (t0 = smbios_next(&ep, NULL); t0; t0 = smbios_next(&ep, t0))
455         if (t0->header.type == 0) {
456             need_t0 = 0;
457             break;
458         }
459 
460     qtables_len = ep.structure_table_length;
461     if (need_t0) {
462         /* common case: add our own type 0, with 3 strings and 4 '\0's */
463         u16 t0_len = sizeof(struct smbios_type_0) + strlen(BIOS_NAME) +
464                      strlen(VERSION) + strlen(BIOS_DATE) + 4;
465         if (t0_len > (0xffff - ep.structure_table_length)) {
466             dprintf(1, "Insufficient space (%d bytes) to add SMBIOS type 0 table (%d bytes)\n",
467                     0xffff - ep.structure_table_length, t0_len);
468             need_t0 = 0;
469         } else {
470             ep.structure_table_length += t0_len;
471             if (t0_len > ep.max_structure_size)
472                 ep.max_structure_size = t0_len;
473             ep.number_of_structures++;
474         }
475     }
476 
477     /* allocate final blob and record its address in the entry point */
478     if (ep.structure_table_length > BUILD_MAX_SMBIOS_FSEG)
479         tables = malloc_high(ep.structure_table_length);
480     else
481         tables = malloc_fseg(ep.structure_table_length);
482     if (!tables) {
483         warn_noalloc();
484         free(qtables);
485         return 0;
486     }
487     ep.structure_table_address = (u32)tables;
488 
489     /* populate final blob */
490     if (need_t0)
491         tables = smbios_new_type_0(tables, BIOS_NAME, VERSION, BIOS_DATE);
492     memcpy(tables, qtables, qtables_len);
493     free(qtables);
494 
495     /* finalize entry point */
496     ep.checksum -= checksum(&ep, 0x10);
497     ep.intermediate_checksum -= checksum((void *)&ep + 0x10, ep.length - 0x10);
498 
499     copy_smbios(&ep);
500     return 1;
501 }
502 
503 void
smbios_setup(void)504 smbios_setup(void)
505 {
506     if (smbios_romfile_setup())
507         return;
508     smbios_legacy_setup();
509 }
510 
511 void
copy_table(void * pos)512 copy_table(void *pos)
513 {
514     copy_pir(pos);
515     copy_mptable(pos);
516     copy_acpi_rsdp(pos);
517     copy_smbios(pos);
518 }
519