blob: 48325a439bf3042aeaf3d0d323ee5ff54ad276e6 [file] [log] [blame]
Kevin O'Connore9161822014-04-05 09:11:45 -04001// Support for manipulating bios tables (pir, mptable, acpi, smbios).
Ian Campbell1442c312011-06-01 11:00:28 +01002//
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
Kevin O'Connor836b4d82014-04-07 15:42:04 -04007#include "byteorder.h" // le32_to_cpu
Kevin O'Connor2d2fa312013-09-14 21:55:26 -04008#include "config.h" // CONFIG_*
Kevin O'Connor9dea5902013-09-14 20:23:54 -04009#include "malloc.h" // malloc_fseg
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040010#include "output.h" // dprintf
Kevin O'Connor836b4d82014-04-07 15:42:04 -040011#include "hw/pci.h" // pci_config_writeb
Kevin O'Connor5a7545c2013-09-14 22:54:44 -040012#include "std/acpi.h" // struct rsdp_descriptor
Kevin O'Connor3d0dfe12013-09-14 22:48:04 -040013#include "std/mptable.h" // MPTABLE_SIGNATURE
Kevin O'Connorc5e06fb2013-09-14 22:22:28 -040014#include "std/pirtable.h" // struct pir_header
Kevin O'Connorb37a5282013-09-14 22:45:05 -040015#include "std/smbios.h" // struct smbios_entry_point
Kevin O'Connorfa9c66a2013-09-14 19:10:40 -040016#include "string.h" // memcpy
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040017#include "util.h" // copy_table
Kevin O'Connor836b4d82014-04-07 15:42:04 -040018#include "x86.h" // outb
Ian Campbell1442c312011-06-01 11:00:28 +010019
Kevin O'Connor4d053eb2012-06-09 13:08:02 -040020static void
Ian Campbell1442c312011-06-01 11:00:28 +010021copy_pir(void *pos)
22{
23 struct pir_header *p = pos;
24 if (p->signature != PIR_SIGNATURE)
25 return;
Kevin O'Connorbfa02cd2012-06-09 13:36:45 -040026 if (PirAddr)
Ian Campbell1442c312011-06-01 11:00:28 +010027 return;
28 if (p->size < sizeof(*p))
29 return;
30 if (checksum(pos, p->size) != 0)
31 return;
32 void *newpos = malloc_fseg(p->size);
33 if (!newpos) {
34 warn_noalloc();
35 return;
36 }
37 dprintf(1, "Copying PIR from %p to %p\n", pos, newpos);
38 memcpy(newpos, pos, p->size);
Kevin O'Connorbfa02cd2012-06-09 13:36:45 -040039 PirAddr = newpos;
Ian Campbell1442c312011-06-01 11:00:28 +010040}
41
Kevin O'Connor4d053eb2012-06-09 13:08:02 -040042static void
Ian Campbell1442c312011-06-01 11:00:28 +010043copy_mptable(void *pos)
44{
45 struct mptable_floating_s *p = pos;
46 if (p->signature != MPTABLE_SIGNATURE)
47 return;
48 if (!p->physaddr)
49 return;
50 if (checksum(pos, sizeof(*p)) != 0)
51 return;
52 u32 length = p->length * 16;
53 u16 mpclength = ((struct mptable_config_s *)p->physaddr)->length;
54 struct mptable_floating_s *newpos = malloc_fseg(length + mpclength);
55 if (!newpos) {
56 warn_noalloc();
57 return;
58 }
59 dprintf(1, "Copying MPTABLE from %p/%x to %p\n", pos, p->physaddr, newpos);
60 memcpy(newpos, pos, length);
61 newpos->physaddr = (u32)newpos + length;
62 newpos->checksum -= checksum(newpos, sizeof(*newpos));
63 memcpy((void*)newpos + length, (void*)p->physaddr, mpclength);
64}
65
Kevin O'Connor836b4d82014-04-07 15:42:04 -040066
67/****************************************************************
68 * ACPI
69 ****************************************************************/
70
Michael S. Tsirkinff5f7922013-10-03 16:30:35 +030071static int
72get_acpi_rsdp_length(void *pos, unsigned size)
73{
74 struct rsdp_descriptor *p = pos;
75 if (p->signature != RSDP_SIGNATURE)
76 return -1;
77 u32 length = 20;
78 if (length > size)
79 return -1;
80 if (checksum(pos, length) != 0)
81 return -1;
82 if (p->revision > 1) {
83 length = p->length;
84 if (length > size)
85 return -1;
86 if (checksum(pos, length) != 0)
87 return -1;
88 }
89 return length;
90}
91
Kevin O'Connor836b4d82014-04-07 15:42:04 -040092struct rsdp_descriptor *RsdpAddr;
93
Kevin O'Connor4d053eb2012-06-09 13:08:02 -040094static void
Ian Campbell1442c312011-06-01 11:00:28 +010095copy_acpi_rsdp(void *pos)
96{
97 if (RsdpAddr)
98 return;
Michael S. Tsirkinff5f7922013-10-03 16:30:35 +030099 int length = get_acpi_rsdp_length(pos, -1);
100 if (length < 0)
Ian Campbell1442c312011-06-01 11:00:28 +0100101 return;
Ian Campbell1442c312011-06-01 11:00:28 +0100102 void *newpos = malloc_fseg(length);
103 if (!newpos) {
104 warn_noalloc();
105 return;
106 }
107 dprintf(1, "Copying ACPI RSDP from %p to %p\n", pos, newpos);
108 memcpy(newpos, pos, length);
109 RsdpAddr = newpos;
110}
Ian Campbell74c78782011-06-01 11:00:29 +0100111
Kevin O'Connor836b4d82014-04-07 15:42:04 -0400112void *find_acpi_rsdp(void)
113{
114 extern u8 zonefseg_start[], zonefseg_end[];
115 unsigned long start = (unsigned long)zonefseg_start;
116 unsigned long end = (unsigned long)zonefseg_end;
117 unsigned long pos;
118
119 for (pos = ALIGN(start, 0x10); pos <= ALIGN_DOWN(end, 0x10); pos += 0x10)
120 if (get_acpi_rsdp_length((void *)pos, end - pos) >= 0)
121 return (void *)pos;
122
123 return NULL;
124}
125
126static struct fadt_descriptor_rev1 *
127find_fadt(void)
128{
129 dprintf(4, "rsdp=%p\n", RsdpAddr);
130 if (!RsdpAddr || RsdpAddr->signature != RSDP_SIGNATURE)
131 return NULL;
132 struct rsdt_descriptor_rev1 *rsdt = (void*)RsdpAddr->rsdt_physical_address;
133 dprintf(4, "rsdt=%p\n", rsdt);
134 if (!rsdt || rsdt->signature != RSDT_SIGNATURE)
135 return NULL;
136 void *end = (void*)rsdt + rsdt->length;
137 int i;
138 for (i=0; (void*)&rsdt->table_offset_entry[i] < end; i++) {
139 struct fadt_descriptor_rev1 *fadt = (void*)rsdt->table_offset_entry[i];
140 if (!fadt || fadt->signature != FACP_SIGNATURE)
141 continue;
142 dprintf(4, "fadt=%p\n", fadt);
143 return fadt;
144 }
145 dprintf(4, "no fadt found\n");
146 return NULL;
147}
148
149u32
150find_resume_vector(void)
151{
152 struct fadt_descriptor_rev1 *fadt = find_fadt();
153 if (!fadt)
154 return 0;
155 struct facs_descriptor_rev1 *facs = (void*)fadt->firmware_ctrl;
156 dprintf(4, "facs=%p\n", facs);
157 if (! facs || facs->signature != FACS_SIGNATURE)
158 return 0;
159 // Found it.
160 dprintf(4, "resume addr=%d\n", facs->firmware_waking_vector);
161 return facs->firmware_waking_vector;
162}
163
164static struct acpi_20_generic_address acpi_reset_reg;
165static u8 acpi_reset_val;
166u32 acpi_pm1a_cnt VARFSEG;
167
168#define acpi_ga_to_bdf(addr) pci_to_bdf(0, (addr >> 32) & 0xffff, (addr >> 16) & 0xffff)
169
170void
171acpi_reboot(void)
172{
173 // Check it passed the sanity checks in acpi_set_reset_reg() and was set
174 if (acpi_reset_reg.register_bit_width != 8)
175 return;
176
177 u64 addr = le64_to_cpu(acpi_reset_reg.address);
178
179 dprintf(1, "ACPI hard reset %d:%llx (%x)\n",
180 acpi_reset_reg.address_space_id, addr, acpi_reset_val);
181
182 switch (acpi_reset_reg.address_space_id) {
183 case 0: // System Memory
184 writeb((void *)(u32)addr, acpi_reset_val);
185 break;
186 case 1: // System I/O
187 outb(acpi_reset_val, addr);
188 break;
189 case 2: // PCI config space
190 pci_config_writeb(acpi_ga_to_bdf(addr), addr & 0xffff, acpi_reset_val);
191 break;
192 }
193}
194
195static void
196acpi_set_reset_reg(struct acpi_20_generic_address *reg, u8 val)
197{
198 if (!reg || reg->address_space_id > 2 ||
199 reg->register_bit_width != 8 || reg->register_bit_offset)
200 return;
201
202 acpi_reset_reg = *reg;
203 acpi_reset_val = val;
204}
205
206void
207find_acpi_features(void)
208{
209 struct fadt_descriptor_rev1 *fadt = find_fadt();
210 if (!fadt)
211 return;
212 u32 pm_tmr = le32_to_cpu(fadt->pm_tmr_blk);
213 u32 pm1a_cnt = le32_to_cpu(fadt->pm1a_cnt_blk);
214 dprintf(4, "pm_tmr_blk=%x\n", pm_tmr);
215 if (pm_tmr)
216 pmtimer_setup(pm_tmr);
217 if (pm1a_cnt)
218 acpi_pm1a_cnt = pm1a_cnt;
219
220 // Theoretically we should check the 'reset_reg_sup' flag, but Windows
221 // doesn't and thus nobody seems to *set* it. If the table is large enough
222 // to include it, let the sanity checks in acpi_set_reset_reg() suffice.
223 if (fadt->length >= 129) {
224 void *p = fadt;
225 acpi_set_reset_reg(p + 116, *(u8 *)(p + 128));
226 }
227}
228
229
230/****************************************************************
231 * SMBIOS
232 ****************************************************************/
233
Kevin O'Connord18c9f02014-04-07 15:48:12 -0400234struct smbios_entry_point *SMBiosAddr;
235
David Woodhouse38b24db2013-01-25 19:33:58 -0600236void
Ian Campbell74c78782011-06-01 11:00:29 +0100237copy_smbios(void *pos)
238{
Kevin O'Connor83012de2011-08-28 12:42:15 -0400239 if (SMBiosAddr)
240 return;
Ian Campbell74c78782011-06-01 11:00:29 +0100241 struct smbios_entry_point *p = pos;
242 if (memcmp(p->anchor_string, "_SM_", 4))
243 return;
244 if (checksum(pos, 0x10) != 0)
245 return;
246 if (memcmp(p->intermediate_anchor_string, "_DMI_", 5))
247 return;
248 if (checksum(pos+0x10, p->length-0x10) != 0)
249 return;
Ian Campbellee2bc462011-06-14 15:22:09 +0100250 struct smbios_entry_point *newpos = malloc_fseg(p->length);
Ian Campbell74c78782011-06-01 11:00:29 +0100251 if (!newpos) {
252 warn_noalloc();
253 return;
254 }
255 dprintf(1, "Copying SMBIOS entry point from %p to %p\n", pos, newpos);
256 memcpy(newpos, pos, p->length);
Kevin O'Connor83012de2011-08-28 12:42:15 -0400257 SMBiosAddr = newpos;
Ian Campbell74c78782011-06-01 11:00:29 +0100258}
Kevin O'Connor4d053eb2012-06-09 13:08:02 -0400259
260void
Kevin O'Connord18c9f02014-04-07 15:48:12 -0400261display_uuid(void)
262{
263 u32 addr, end;
264 u8 *uuid;
265 u8 empty_uuid[16] = { 0 };
266
267 if (SMBiosAddr == NULL)
268 return;
269
270 addr = SMBiosAddr->structure_table_address;
271 end = addr + SMBiosAddr->structure_table_length;
272
273 /* the following takes care of any initial wraparound too */
274 while (addr < end) {
275 const struct smbios_structure_header *hdr;
276
277 /* partial structure header */
278 if (end - addr < sizeof(struct smbios_structure_header))
279 return;
280
281 hdr = (struct smbios_structure_header *)addr;
282
283 /* partial structure */
284 if (end - addr < hdr->length)
285 return;
286
287 /* any Type 1 structure version will do that has the UUID */
288 if (hdr->type == 1 &&
289 hdr->length >= offsetof(struct smbios_type_1, uuid) + 16)
290 break;
291
292 /* done with formatted area, skip string-set */
293 addr += hdr->length;
294
295 while (end - addr >= 2 &&
296 (*(u8 *)addr != '\0' ||
297 *(u8 *)(addr+1) != '\0'))
298 ++addr;
299
300 /* structure terminator not found */
301 if (end - addr < 2)
302 return;
303
304 addr += 2;
305 }
306
307 /* parsing finished or skipped entirely, UUID not found */
308 if (addr >= end)
309 return;
310
311 uuid = (u8 *)(addr + offsetof(struct smbios_type_1, uuid));
312 if (memcmp(uuid, empty_uuid, sizeof empty_uuid) == 0)
313 return;
314
315 printf("Machine UUID"
316 " %02x%02x%02x%02x"
317 "-%02x%02x"
318 "-%02x%02x"
319 "-%02x%02x"
320 "-%02x%02x%02x%02x%02x%02x\n"
321 , uuid[ 0], uuid[ 1], uuid[ 2], uuid[ 3]
322 , uuid[ 4], uuid[ 5]
323 , uuid[ 6], uuid[ 7]
324 , uuid[ 8], uuid[ 9]
325 , uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
326}
327
328
329void
Kevin O'Connor4d053eb2012-06-09 13:08:02 -0400330copy_table(void *pos)
331{
332 copy_pir(pos);
333 copy_mptable(pos);
334 copy_acpi_rsdp(pos);
335 copy_smbios(pos);
336}