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