From 51bf66f750a32dedc387c7ba4d0fc7059b70b10e Mon Sep 17 00:00:00 2001 From: Vladimir Koshelev Date: Mon, 26 Sep 2011 13:53:41 +0400 Subject: [PATCH 17/32] Add virtual smbios device support --- palacios/src/devices/Kconfig | 11 + palacios/src/devices/Makefile | 2 + palacios/src/devices/smbios.c | 476 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 489 insertions(+), 0 deletions(-) create mode 100644 palacios/src/devices/smbios.c diff --git a/palacios/src/devices/Kconfig b/palacios/src/devices/Kconfig index b6543a6..e98e016 100644 --- a/palacios/src/devices/Kconfig +++ b/palacios/src/devices/Kconfig @@ -485,5 +485,16 @@ config CHAR_STREAM help Include Stream based character device frontend +config SMBIOS + bool "SMBios" + default n + +config SMBIOS_DEBUG + bool "Debug SMBios virtualization" + default n + depends on SMBIOS + + + endmenu diff --git a/palacios/src/devices/Makefile b/palacios/src/devices/Makefile index 70c9be1..03ba8d0 100644 --- a/palacios/src/devices/Makefile +++ b/palacios/src/devices/Makefile @@ -48,3 +48,5 @@ obj-$(V3_CONFIG_VGA) += vga.o obj-$(V3_CONFIG_PCI_FRONT) += pci_front.o +obj-$(V3_CONFIG_SMBIOS) += smbios.o + diff --git a/palacios/src/devices/smbios.c b/palacios/src/devices/smbios.c new file mode 100644 index 0000000..4fa6169 --- /dev/null +++ b/palacios/src/devices/smbios.c @@ -0,0 +1,476 @@ +/* + * This file is part of the Palacios Virtual Machine Monitor developed + * by the V3VEE Project with funding from the United States National + * Science Foundation and the Department of Energy. + * + * The V3VEE Project is a joint project between Northwestern University + * and the University of New Mexico. You can find out more at + * http://www.v3vee.org + * + * Copyright (c) 2010, Peter Dinda + * Copyright (c) 2008, The V3VEE Project + * All rights reserved. + * + * Author: Peter Dinda + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "V3VEE_LICENSE". + */ + +#ifdef __V3VEE__ + +#include +#include +#include +#include + +#define SMBIOS_DEFAULT_LOCATION 0xFF000 +#define SMBIOS_TABLE_LOCATION 0xC7000 +//#define SMBIOS_TABLE_LOCATION 0xF37FE000 + +#define V3_CONFIG_SMBIOS_USE_MEM_HOOK + + +struct smbios_entry_point { + uint8_t archos_string[4]; + uint8_t eps; //Entry point checksum + uint8_t epl; //Entry point length + uint8_t major; + uint8_t minor; + uint16_t max_struct_size; + uint8_t entry_point_revision; + uint8_t formated_area[5]; + uint8_t iarchos_string[5]; + uint8_t ieps; + uint16_t struct_table_length; + uint32_t struct_table_pointer; + uint8_t number_smbion_struct; + uint8_t smbios_bcd_revision; +} __attribute__((packed)); + +struct smbios_structure { + uint8_t type; + uint8_t length; + uint16_t handle; +} __attribute__((packed)); + +struct smbios_list { + struct list_head list_head; + void * data; +}; + +static struct list_head structs[256]; + +static int checksum(uint8_t *buf, uint32_t len) { + uint8_t sum = 0; + uint32_t i; + for (i = 0; i < len; i++) + sum += buf[i]; + return sum == 0; +} + +static uint8_t calc_checksum(uint8_t *buf, uint32_t len) { + uint8_t sum = 0; + uint32_t i; + for (i = 0; i < len; i++) + sum += buf[i]; + return (0x100 - sum) & 0xff; + +} + +static int erase_element(uint8_t type, uint16_t handle) { + struct smbios_list* pos; + list_for_each_entry(pos, &structs[type], list_head) { + struct smbios_structure * smbios = pos->data; + if (smbios->handle == handle) { + list_del(&pos->list_head); + V3_Free(pos); + return 0; + } + } + return -1; +} + +static int add_element(uint8_t type, uint16_t handle, struct list_head *head) { + struct smbios_list* pos; + list_for_each_entry(pos, &structs[type], list_head) { + struct smbios_structure * smbios = pos->data; + if (smbios->handle == handle) { + list_del(&pos->list_head); + list_add(&pos->list_head, head); + return 0; + } + } + return -1; +} + +#ifdef V3_CONFIG_SMBIOS_USE_MEM_HOOK + +static int smbios_read(struct guest_info * core, addr_t guest_addr, void * dst, uint_t length, void * priv_data) { + +// PrintDebug("smbios read from %lX length %Xn data %lX\n", guest_addr, length, + // *((addr_t *)(priv_data + (guest_addr - SMBIOS_TABLE_LOCATION)))); + memcpy(dst, priv_data + (guest_addr - SMBIOS_TABLE_LOCATION), length); + return 0; +} +static int smbios_write (struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data) { + PrintError("Unexpected smbios write...\n"); + return 0; +} +#endif + +static int smbios_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) { + uint32_t i; + int include_all_flag; + + for (i = 0; i < 256; i++) { + INIT_LIST_HEAD(&structs[i]); + } + + char * dumpfile = v3_cfg_val(cfg, "dump_file"); + struct smbios_entry_point * entry_point; + + if (dumpfile == NULL) { + PrintError("Can't found dump_file tag\n"); + return -1; + } + struct v3_cfg_file * file = v3_cfg_get_file(vm, dumpfile); + + if (file == NULL) { + PrintError("Can't get dump file by name %s", dumpfile); + return -1; + } + if (file->size < 0x20) { + PrintError("File corrupted, exiting...\n"); + return -1; + } + entry_point = file->data; + + +#ifdef V3_CONFIG_SMBIOS_DEBUG + +#define DUMP8(X) PrintDebug(" Entry_point."#X " = %02hhX\n", entry_point->X); +#define DUMP16(X) PrintDebug(" Entry_point."#X " = %04hX\n", entry_point->X); +#define DUMP32(X) PrintDebug(" Entry_point."#X " = %08X\n", entry_point->X); +#define DUMP_ARCHOS4(X)\ + PrintDebug(" Entry_point."#X " = %c%c%c%c\n",\ + entry_point->X[0],entry_point->X[1],entry_point->X[2],entry_point->X[3]); +#define DUMP_ARCHOS5(X)\ + PrintDebug(" Entry_point."#X " = %c%c%c%c%c\n",\ + entry_point->X[0],entry_point->X[1],entry_point->X[2],entry_point->X[3],entry_point->X[4]); + + DUMP_ARCHOS4(archos_string) + DUMP8(eps) + DUMP8(epl); + DUMP8(major); + DUMP8(minor); + DUMP16(max_struct_size); + DUMP8(entry_point_revision); + DUMP_ARCHOS5(iarchos_string); + DUMP8(ieps); + DUMP16(struct_table_length); + DUMP32(struct_table_pointer); + DUMP8( number_smbion_struct); + DUMP8( smbios_bcd_revision); + + +#undef DUMP8 +#undef DUMP16 +#undef DUMP32 +#undef DUMP_ARCHOS4 +#undef DUMP_ARCHOS5 + + +#endif + + if (memcmp(entry_point->archos_string, "_SM_", 4) != 0 + || memcmp(entry_point->iarchos_string, "_DMI_", 5) != 0) { + PrintError("Wrong signature in entry point\n"); + return -1; + } + if (!checksum((void *)entry_point, entry_point->epl) || !checksum(((void *)entry_point) + 0x10, 0x0f) ) { + PrintError("Wrong checksum, exiting...\n"); + return -1; + } + +#ifdef V3_CONFIG_SMBIOS_DEBUG + + PrintDebug("Parsing table...\n"); + +#endif + + char * data = file->data + 0x20; + i = 0; + while ((i < entry_point->number_smbion_struct) + && ((void *)data + 4 < (void*)file->data + file->size)) { + + struct smbios_structure * s = (struct smbios_structure *) data; + if (s->length < 4) { + PrintError("Structure has wrong length, exiting... "); + return -1; + } + struct smbios_list * elem = (struct smbios_list*) V3_Malloc(sizeof(struct smbios_list)); + if (elem == NULL) { + PrintError("Malloc return NULL!\n"); + return -1; + } + elem->data = data; +#ifdef V3_CONFIG_SMBIOS_DEBUG + PrintDebug("Structure: type : %02hhX size : %02hhX handle %04hX\n", s->type, s->length, s->handle); +#endif + list_add(&elem->list_head, &structs[s->type]); + + data = data + s->length; + while ((void *)data - (void *) file->data + 1 < file->size + && (data[0] != 0 || data[1] != 0)) + data++; + + data +=2; + i++; + + } + + if (i != entry_point->number_smbion_struct) { + PrintError("Wrong number of smbion structures!\n"); + return -1; + } + +#ifdef V3_CONFIG_SMBIOS_DEBUG + + PrintDebug("Parsing configuration space...\n"); + +#endif + + + v3_cfg_tree_t * include_all = v3_cfg_subtree(cfg, "include_all"); + + include_all_flag = include_all != NULL; + + struct list_head result; + + INIT_LIST_HEAD(&result); + + + if (include_all_flag) { + +#ifdef V3_CONFIG_SMBIOS_DEBUG + + PrintDebug("Flag include all is on...\n"); + +#endif + + v3_cfg_tree_t * current = v3_cfg_subtree(include_all, "exclude"); + while (current) { + + char * type = v3_cfg_val(current, "type"); + char * handle = v3_cfg_val(current, "handle"); + uint64_t tmp = atox(type); + if (tmp > 0xff) { + PrintError("Type parameter is too large\n"); + return -1; + } + uint8_t type_value = (uint8_t) tmp; + tmp = atox(handle); + + if (tmp > 0xffff) { + PrintError("Handle parameter is too large\n"); + return -1; + } + + uint16_t handle_value = (uint16_t) tmp; + + if (erase_element(type_value, handle_value)) { + PrintError("Warning : record %d.%d not found!", + type_value, handle_value); + } + + current = v3_cfg_next_branch(current); + } + +#ifdef V3_CONFIG_SMBIOS_DEBUG + + PrintDebug("Dumping...\n"); + +#endif + + + for (i = 0; i <= 0xff; i++) { + struct list_head *pos = structs[i].next; + while (pos != &structs[i]) { + struct list_head *tmp = pos->next; + list_add(pos, &result); + pos = tmp; + } + } + + } else { + +#ifdef V3_CONFIG_SMBIOS_DEBUG + + PrintDebug("Flag include all is off...\n"); + +#endif + + v3_cfg_tree_t * current = v3_cfg_subtree(cfg, "include"); + while (current) { + + char * type = v3_cfg_val(current, "type"); + char * handle = v3_cfg_val(current, "handle"); + uint64_t tmp = atox(type); + if (tmp > 0xff) { + PrintError("Type parameter is too large\n"); + return -1; + } + uint8_t type_value = (uint8_t) tmp; + tmp = atox(handle); + + if (tmp > 0xffff) { + PrintError("Handle parameter is too large\n"); + return -1; + } + + uint16_t handle_value = (uint16_t) tmp; + + if (add_element(type_value, handle_value, &result)) { + PrintError("Can't add %02hhX.%04hX to smbios. Structure not found\n", type_value, handle_value); + } + + current = v3_cfg_next_branch(current); + } + } + +#ifdef V3_CONFIG_SMBIOS_DEBUG + + PrintDebug("Allocing pages...\n"); + +#endif + + + uint16_t page_count = entry_point->struct_table_length / PAGE_SIZE_4KB+ + (entry_point->struct_table_length % PAGE_SIZE_4KB != 0); + + uint8_t * pages = V3_VAddr(V3_AllocPages(page_count)); + + struct smbios_list *pos; + uint8_t count = 0; + uint16_t length = 0; + list_for_each_entry(pos, &result, list_head) { + //PrintDebug("Adding new structure %hhX %hhX %hX\n", ((struct smbios_structure *)(pos->data))->type, + // ((struct smbios_structure *)(pos->data))->length , + // ((struct smbios_structure *)(pos->data))->handle); + count++; + if (count == 0) { + PrintError("Count overflow error...\n"); + return -1; + } + uint8_t * pointer; + uint32_t slen = ((struct smbios_structure *)(pos->data))->length; + memcpy(pages + length, pos->data, slen); + length += slen; + pointer = pos->data + slen; + while (pointer[0] != 0 || pointer[1] != 0) { + pages[length] = *pointer; + pointer++; + length++; + if (length == 0) { + PrintError("Length overflow error...\n"); + return -1; + } + } + pages[length++] = 0; + pages[length++] = 0; + pointer += 2; + } + + entry_point->number_smbion_struct = count; + entry_point->struct_table_length = length; + entry_point->struct_table_pointer = SMBIOS_TABLE_LOCATION; + +#ifdef V3_CONFIG_SMBIOS_DEBUG + +#define DUMP8(X) PrintDebug(" Entry_point."#X " = %02hhX\n", entry_point->X); +#define DUMP16(X) PrintDebug(" Entry_point."#X " = %04hX\n", entry_point->X); +#define DUMP32(X) PrintDebug(" Entry_point."#X " = %08X\n", entry_point->X); +#define DUMP_ARCHOS4(X)\ + PrintDebug(" Entry_point."#X " = %c%c%c%c\n",\ + entry_point->X[0],entry_point->X[1],entry_point->X[2],entry_point->X[3]); +#define DUMP_ARCHOS5(X)\ + PrintDebug(" Entry_point."#X " = %c%c%c%c%c\n",\ + entry_point->X[0],entry_point->X[1],entry_point->X[2],entry_point->X[3],entry_point->X[4]); + + DUMP_ARCHOS4(archos_string) + DUMP8(eps) + DUMP8(epl); + DUMP8(major); + DUMP8(minor); + DUMP16(max_struct_size); + DUMP8(entry_point_revision); + DUMP_ARCHOS5(iarchos_string); + DUMP8(ieps); + DUMP16(struct_table_length); + DUMP32(struct_table_pointer); + DUMP8( number_smbion_struct); + DUMP8( smbios_bcd_revision); + +#undef DUMP8 +#undef DUMP16 +#undef DUMP32 +#undef DUMP_ARCHOS4 +#undef DUMP_ARCHOS5 + +#endif + +#ifdef V3_CONFIG_SMBIOS_DEBUG + + PrintDebug("Adding shadow memory region...\n"); + +#endif + +#ifdef V3_CONFIG_SMBIOS_USE_MEM_HOOK + if (v3_hook_full_mem(vm, V3_MEM_CORE_ANY, entry_point->struct_table_pointer, + (addr_t) entry_point->struct_table_pointer + PAGE_SIZE * page_count - 1, + smbios_read, smbios_write, pages)) { + PrintError("Can't setup memhook...\n"); + return 0; + } +#else + + if (v3_add_shadow_mem(vm, V3_MEM_CORE_ANY, (addr_t) entry_point->struct_table_pointer, + (addr_t) entry_point->struct_table_pointer + PAGE_SIZE * page_count - 1,(addr_t) pages)) { + PrintError("Can't add memory region..."); + return -1; + } +#endif + + entry_point->ieps = 0; + entry_point->ieps = calc_checksum(((void *)entry_point) + 0x10, 0x0f); + entry_point->eps = 0; + entry_point->eps = calc_checksum(((void *)entry_point), entry_point->epl); + + + void * target; + + if (v3_gpa_to_hva(&(vm->cores[0]), + SMBIOS_DEFAULT_LOCATION , (addr_t *)&target) == -1) { + PrintError("Cannot inject mptable due to unmapped bios!\n"); + return -1; + } + + memcpy(target, entry_point, entry_point->epl); + + if (memcmp(target, "_SM_", 4) != 0 || + memcmp(target + 0x10, "_DMI_", 5) != 0) { + PrintError("Wrong signature in memory\n"); + return -1; + } + +#ifdef V3_CONFIG_SMBIOS_DEBUG + PrintDebug("Initialization complete.\n"); +#endif + + return 0; +} + + +device_register("SMBIOS", smbios_init) +#endif -- 1.7.5.4