The protocol we use between qemu and seabios already allows any field to be specified (via smbios_add_field() in qemu). This patch makes seabios look for qemu specified values for nearly every field we set in the types 0,1,3,4,16,17,32 smbios tables. No change in current default values for any fields.
Signed-off-by: Alex Williamson alex.williamson@redhat.com ---
src/smbios.c | 222 +++++++++++++++++++++++++++++++++++++--------------------- 1 files changed, 141 insertions(+), 81 deletions(-)
diff --git a/src/smbios.c b/src/smbios.c index e19a62d..917428a 100644 --- a/src/smbios.c +++ b/src/smbios.c @@ -61,7 +61,7 @@ smbios_entry_point_init(u16 max_structure_size, p->field = ++str_index; \ } while (0)
-#define load_str_field_or_skip(type, field) \ +#define load_str_field_or_skip(type, field) \ do { \ size = qemu_cfg_smbios_load_field(type, \ offsetof(struct smbios_type_##type, \ @@ -74,6 +74,15 @@ smbios_entry_point_init(u16 max_structure_size, } \ } while (0)
+#define set_field_with_default(type, field, def) \ + do { \ + if (!qemu_cfg_smbios_load_field(type, \ + offsetof(struct smbios_type_##type, \ + field), &p->field)) { \ + p->field = def; \ + } \ + } while (0) + /* Type 0 -- BIOS Information */ #define RELEASE_DATE_STR "01/01/2007" static void * @@ -97,24 +106,26 @@ smbios_init_type_0(void *start)
p->bios_rom_size = 0; /* FIXME */
- memset(p->bios_characteristics, 0, 8); - p->bios_characteristics[0] = 0x08; /* BIOS characteristics not supported */ - p->bios_characteristics_extension_bytes[0] = 0; - /* Enable targeted content distribution. Needed for SVVP */ - p->bios_characteristics_extension_bytes[1] = 4; - if (!qemu_cfg_smbios_load_field(0, offsetof(struct smbios_type_0, - system_bios_major_release), - &p->system_bios_major_release)) - p->system_bios_major_release = 1; + bios_characteristics), + &p->bios_characteristics)) { + memset(p->bios_characteristics, 0, 8); + /* BIOS characteristics not supported */ + p->bios_characteristics[0] = 0x08; + }
if (!qemu_cfg_smbios_load_field(0, offsetof(struct smbios_type_0, - system_bios_minor_release), - &p->system_bios_minor_release)) - p->system_bios_minor_release = 0; + bios_characteristics_extension_bytes), + &p->bios_characteristics_extension_bytes)) { + p->bios_characteristics_extension_bytes[0] = 0; + /* Enable targeted content distribution. Needed for SVVP */ + p->bios_characteristics_extension_bytes[1] = 4; + }
- p->embedded_controller_major_release = 0xff; - p->embedded_controller_minor_release = 0xff; + set_field_with_default(0, system_bios_major_release, 1); + set_field_with_default(0, system_bios_minor_release, 0); + set_field_with_default(0, embedded_controller_major_release, 0xff); + set_field_with_default(0, embedded_controller_minor_release, 0xff);
*end = 0; end++; @@ -140,12 +151,12 @@ smbios_init_type_1(void *start) load_str_field_or_skip(1, version_str); load_str_field_or_skip(1, serial_number_str);
- size = qemu_cfg_smbios_load_field(1, offsetof(struct smbios_type_1, - uuid), &p->uuid); - if (size == 0) + if (!qemu_cfg_smbios_load_field(1, offsetof(struct smbios_type_1, + uuid), &p->uuid)) { memset(p->uuid, 0, 16); + }
- p->wake_up_type = 0x06; /* power switch */ + set_field_with_default(1, wake_up_type, 0x06); /* power switch */
load_str_field_or_skip(1, sku_number_str); load_str_field_or_skip(1, family_str); @@ -165,29 +176,39 @@ static void * smbios_init_type_3(void *start) { struct smbios_type_3 *p = (struct smbios_type_3 *)start; + char *end = (char *)start + sizeof(struct smbios_type_3); + size_t size; + int str_index = 0;
p->header.type = 3; p->header.length = sizeof(struct smbios_type_3); p->header.handle = 0x300;
- p->manufacturer_str = 1; - p->type = 0x01; /* other */ - p->version_str = 0; - p->serial_number_str = 0; - p->asset_tag_number_str = 0; - p->boot_up_state = 0x03; /* safe */ - p->power_supply_state = 0x03; /* safe */ - p->thermal_state = 0x03; /* safe */ - p->security_status = 0x02; /* unknown */ - p->oem_defined = 0; - p->height = 0; - p->number_of_power_cords = 0; - p->contained_element_count = 0; - - start += sizeof(struct smbios_type_3); - memcpy((char *)start, CONFIG_APPNAME"\0\0", sizeof(CONFIG_APPNAME) + 1); - - return start + sizeof(CONFIG_APPNAME) + 1; + load_str_field_with_default(3, manufacturer_str, CONFIG_APPNAME); + set_field_with_default(3, type, 0x01); /* other */ + + load_str_field_or_skip(3, version_str); + load_str_field_or_skip(3, serial_number_str); + load_str_field_or_skip(3, asset_tag_number_str); + + set_field_with_default(3, boot_up_state, 0x03); /* safe */ + set_field_with_default(3, power_supply_state, 0x03); /* safe */ + set_field_with_default(3, thermal_state, 0x03); /* safe */ + set_field_with_default(3, security_status, 0x02); /* unknown */ + + set_field_with_default(3, oem_defined, 0); + set_field_with_default(3, height, 0); + set_field_with_default(3, number_of_power_cords, 0); + set_field_with_default(3, contained_element_count, 0); + + *end = 0; + end++; + if (!str_index) { + *end = 0; + end++; + } + + return end; }
/* Type 4 -- Processor Information */ @@ -195,42 +216,63 @@ static void * smbios_init_type_4(void *start, unsigned int cpu_number) { struct smbios_type_4 *p = (struct smbios_type_4 *)start; + char *end = (char *)start + sizeof(struct smbios_type_4); + size_t size; + int str_index = 0; + char name[1024];
p->header.type = 4; p->header.length = sizeof(struct smbios_type_4); p->header.handle = 0x400 + cpu_number;
- p->socket_designation_str = 1; - p->processor_type = 0x03; /* CPU */ - p->processor_family = 0x01; /* other */ - p->processor_manufacturer_str = 2; - - u32 cpuid_signature, ebx, ecx, cpuid_features; - cpuid(1, &cpuid_signature, &ebx, &ecx, &cpuid_features); - p->processor_id[0] = cpuid_signature; - p->processor_id[1] = cpuid_features; - - p->processor_version_str = 0; - p->voltage = 0; - p->external_clock = 0; + size = qemu_cfg_smbios_load_field(4, offsetof(struct smbios_type_4, + socket_designation_str), + name); + if (size) + snprintf(name + size - 1, sizeof(name) - size, "%2x", cpu_number); + else + snprintf(name, sizeof(name), "CPU%2x", cpu_number); + + memcpy(end, name, strlen(name) + 1); + end += strlen(name) + 1; + p->socket_designation_str = ++str_index; + + set_field_with_default(4, processor_type, 0x03); /* CPU */ + set_field_with_default(4, processor_family, 0x01); /* other */ + + load_str_field_with_default(4, processor_manufacturer_str, CONFIG_APPNAME); + + if (!qemu_cfg_smbios_load_field(4, offsetof(struct smbios_type_4, + processor_id), p->processor_id)) { + u32 cpuid_signature, ebx, ecx, cpuid_features; + cpuid(1, &cpuid_signature, &ebx, &ecx, &cpuid_features); + p->processor_id[0] = cpuid_signature; + p->processor_id[1] = cpuid_features; + }
- p->max_speed = 2000; - p->current_speed = 2000; + load_str_field_or_skip(4, processor_version_str); + set_field_with_default(4, voltage, 0); + set_field_with_default(4, external_clock, 0);
- p->status = 0x41; /* socket populated, CPU enabled */ - p->processor_upgrade = 0x01; /* other */ + set_field_with_default(4, max_speed, 2000); + set_field_with_default(4, current_speed, 2000);
- p->l1_cache_handle = 0xffff; /* cache information structure not provided */ - p->l2_cache_handle = 0xffff; - p->l3_cache_handle = 0xffff; + set_field_with_default(4, status, 0x41); /* socket populated, CPU enabled */ + set_field_with_default(4, processor_upgrade, 0x01); /* other */
- start += sizeof(struct smbios_type_4); + /* cache information structure not provided */ + p->l1_cache_handle = 0xffff; + p->l2_cache_handle = 0xffff; + p->l3_cache_handle = 0xffff;
- snprintf((char*)start, 6, "CPU%2x", cpu_number); - start += 6; - memcpy((char *)start, CONFIG_APPNAME"\0\0", sizeof(CONFIG_APPNAME) + 1); + *end = 0; + end++; + if (!str_index) { + *end = 0; + end++; + }
- return start + sizeof(CONFIG_APPNAME) + 1; + return end; }
/* Type 16 -- Physical Memory Array */ @@ -243,9 +285,10 @@ smbios_init_type_16(void *start, u32 memory_size_mb, int nr_mem_devs) p->header.length = sizeof(struct smbios_type_16); p->header.handle = 0x1000;
- p->location = 0x01; /* other */ - p->use = 0x03; /* system memory */ - p->error_correction = 0x06; /* Multi-bit ECC to make Microsoft happy */ + set_field_with_default(16, location, 0x01); /* other */ + set_field_with_default(16, use, 0x03); /* system memory */ + /* Multi-bit ECC to make Microsoft happy */ + set_field_with_default(16, error_correction, 0x06); /* 0x80000000 = unknown, accept sizes < 2TB - TODO multiple arrays */ p->maximum_capacity = memory_size_mb < 2 << 20 ? memory_size_mb << 10 : 0x80000000; @@ -263,30 +306,47 @@ static void * smbios_init_type_17(void *start, u32 size_mb, int instance) { struct smbios_type_17 *p = (struct smbios_type_17 *)start; + char *end = (char *)start + sizeof(struct smbios_type_17); + size_t size; + int str_index = 0; + char name[1024];
p->header.type = 17; p->header.length = sizeof(struct smbios_type_17); p->header.handle = 0x1100 + instance;
p->physical_memory_array_handle = 0x1000; - p->total_width = 64; - p->data_width = 64; + set_field_with_default(17, total_width, 64); + set_field_with_default(17, data_width, 64); /* TODO: should assert in case something is wrong ASSERT((memory_size_mb & ~0x7fff) == 0); */ p->size = size_mb; - p->form_factor = 0x09; /* DIMM */ + set_field_with_default(17, form_factor, 0x09); /* DIMM */ p->device_set = 0; - p->device_locator_str = 1; - p->bank_locator_str = 0; - p->memory_type = 0x07; /* RAM */ - p->type_detail = 0; - - start += sizeof(struct smbios_type_17); - memcpy((char *)start, "DIMM 0", 7); - ((char*)start)[5] += instance; - start += 7; - *((u8 *)start) = 0; - - return start+1; + + size = qemu_cfg_smbios_load_field(17, offsetof(struct smbios_type_17, + device_locator_str), + name); + if (size) + snprintf(name + size - 1, sizeof(name) - size, "%d", instance); + else + snprintf(name, sizeof(name), "DIMM %d", instance); + + memcpy(end, name, strlen(name) + 1); + end += strlen(name) + 1; + p->device_locator_str = ++str_index; + + load_str_field_or_skip(17, bank_locator_str); + set_field_with_default(17, memory_type, 0x07); /* RAM */ + set_field_with_default(17, type_detail, 0); + + *end = 0; + end++; + if (!str_index) { + *end = 0; + end++; + } + + return end; }
/* Type 19 -- Memory Array Mapped Address */ @@ -345,7 +405,7 @@ smbios_init_type_32(void *start) p->header.length = sizeof(struct smbios_type_32); p->header.handle = 0x2000; memset(p->reserved, 0, 6); - p->boot_status = 0; /* no errors detected */ + set_field_with_default(32, boot_status, 0); /* no errors detected */
start += sizeof(struct smbios_type_32); *((u16 *)start) = 0;
On Mon, Jun 21, 2010 at 09:46:19AM -0600, Alex Williamson wrote:
The protocol we use between qemu and seabios already allows any field to be specified (via smbios_add_field() in qemu). This patch makes seabios look for qemu specified values for nearly every field we set in the types 0,1,3,4,16,17,32 smbios tables. No change in current default values for any fields.
What's the use case for this?
Also, I feel obligated to ask the question - if we want qemu to be able to override any smbios field, wouldn't it be simpler to just have qemu pass the smbios table?
-Kevin
On Mon, 2010-06-21 at 23:06 -0400, Kevin O'Connor wrote:
On Mon, Jun 21, 2010 at 09:46:19AM -0600, Alex Williamson wrote:
The protocol we use between qemu and seabios already allows any field to be specified (via smbios_add_field() in qemu). This patch makes seabios look for qemu specified values for nearly every field we set in the types 0,1,3,4,16,17,32 smbios tables. No change in current default values for any fields.
What's the use case for this?
Also, I feel obligated to ask the question - if we want qemu to be able to override any smbios field, wouldn't it be simpler to just have qemu pass the smbios table?
We can already do nearly all of type 0 & 1 fields. We were looking at setting some of the type 3 and 4 fields, like the manufacturer strings. Rather than add support for a field here, another there, I thought I might as well add support for everything I can since the infrastructure to change them from qemu is already in place. At some point it might make sense to move all of the smbios generating code into qemu, but it's mighty convenient for now to just be able to poke in specific fields. Distros might want to do this for consistency or customization. Developers might use this for being able to create a system with arbitrary DMI changes (ex. did my system boot from the power switch, modem ring, or a/c cycle). Thanks,
Alex
On Mon, Jun 21, 2010 at 09:32:36PM -0600, Alex Williamson wrote:
On Mon, 2010-06-21 at 23:06 -0400, Kevin O'Connor wrote:
On Mon, Jun 21, 2010 at 09:46:19AM -0600, Alex Williamson wrote:
The protocol we use between qemu and seabios already allows any field to be specified (via smbios_add_field() in qemu). This patch makes seabios look for qemu specified values for nearly every field we set in the types 0,1,3,4,16,17,32 smbios tables. No change in current default values for any fields.
What's the use case for this?
Also, I feel obligated to ask the question - if we want qemu to be able to override any smbios field, wouldn't it be simpler to just have qemu pass the smbios table?
We can already do nearly all of type 0 & 1 fields. We were looking at setting some of the type 3 and 4 fields, like the manufacturer strings. Rather than add support for a field here, another there, I thought I might as well add support for everything I can since the infrastructure to change them from qemu is already in place. At some point it might make sense to move all of the smbios generating code into qemu, but it's mighty convenient for now to just be able to poke in specific fields. Distros might want to do this for consistency or customization. Developers might use this for being able to create a system with arbitrary DMI changes (ex. did my system boot from the power switch, modem ring, or a/c cycle). Thanks,
Thanks. Do you know if we're likely to see additional types (ie, something other than 0,1,3,4,16,17,32)?
-Kevin
On Tue, 2010-06-22 at 00:03 -0400, Kevin O'Connor wrote:
On Mon, Jun 21, 2010 at 09:32:36PM -0600, Alex Williamson wrote:
On Mon, 2010-06-21 at 23:06 -0400, Kevin O'Connor wrote:
On Mon, Jun 21, 2010 at 09:46:19AM -0600, Alex Williamson wrote:
The protocol we use between qemu and seabios already allows any field to be specified (via smbios_add_field() in qemu). This patch makes seabios look for qemu specified values for nearly every field we set in the types 0,1,3,4,16,17,32 smbios tables. No change in current default values for any fields.
What's the use case for this?
Also, I feel obligated to ask the question - if we want qemu to be able to override any smbios field, wouldn't it be simpler to just have qemu pass the smbios table?
We can already do nearly all of type 0 & 1 fields. We were looking at setting some of the type 3 and 4 fields, like the manufacturer strings. Rather than add support for a field here, another there, I thought I might as well add support for everything I can since the infrastructure to change them from qemu is already in place. At some point it might make sense to move all of the smbios generating code into qemu, but it's mighty convenient for now to just be able to poke in specific fields. Distros might want to do this for consistency or customization. Developers might use this for being able to create a system with arbitrary DMI changes (ex. did my system boot from the power switch, modem ring, or a/c cycle). Thanks,
Thanks. Do you know if we're likely to see additional types (ie, something other than 0,1,3,4,16,17,32)?
We already supply a pretty good set of the basic types. I won't rule out adding new ones, but it doesn't seem likely. If someone wants to add another for a special case, we do support loading arbitrary tables as a complete chunk via the same interface. Commit b6f6e3d3 in the qemu tree describes how to extract a table from a physical system and add it to a qemu VM.
I will note that type 16 & 17 fields are somewhat partial coverage because I don't allow overriding the memory size and layout. This seems like a step beyond picking a different type from an enum or replacing a string. Likewise, we supply type 19 & 20 tables, but these are entirely based on memory layout, so I'm not sure it makes sense to allow field setting. The table load option is still available should someone want to poke these. Thanks,
Alex
On Mon, Jun 21, 2010 at 10:29:06PM -0600, Alex Williamson wrote:
On Tue, 2010-06-22 at 00:03 -0400, Kevin O'Connor wrote:
Thanks. Do you know if we're likely to see additional types (ie, something other than 0,1,3,4,16,17,32)?
We already supply a pretty good set of the basic types. I won't rule out adding new ones, but it doesn't seem likely.
Thanks. If there are no further comments, I'll commit this in the next couple of days.
-Kevin
On Mon, Jun 21, 2010 at 09:46:19AM -0600, Alex Williamson wrote:
The protocol we use between qemu and seabios already allows any field to be specified (via smbios_add_field() in qemu). This patch makes seabios look for qemu specified values for nearly every field we set in the types 0,1,3,4,16,17,32 smbios tables. No change in current default values for any fields.
Thanks. Commit 17d3e465.
-Kevin