| /* |
| * Copyright 2010 Google Inc. All Rights Reserved. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation; either version 2 |
| * of the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
| * |
| * Ported from mosys project (http://code.google.com/p/mosys/) |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <inttypes.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <fcntl.h> |
| #include <ctype.h> |
| #include <uuid/uuid.h> |
| |
| #include "lib/math.h" |
| #include "lib/lib_smbios.h" |
| #include "lib/vpd.h" |
| #include "lib/vpd_tables.h" |
| |
| /* |
| * This function simply looks for the pattern of two adjacent NULL bytes |
| * following the table header. |
| */ |
| int vpd_sizeof_strings(void *table) |
| { |
| uint8_t *p; |
| struct vpd_header *header = table; |
| size_t size = 0, offset = 0; |
| unsigned char cmp[2] = { '\0', '\0' }; |
| uint8_t found = 0; |
| |
| /* |
| * Search for double NULL. End of strings will be one byte before the |
| * final terminator which indicates end of structure. |
| */ |
| for (p = table + header->length - 1; p; p++, offset++) { |
| if (!memcmp(p, cmp, 2)) { |
| found = 1; |
| break; |
| } |
| } |
| |
| if (found) |
| size = offset; |
| |
| return size; |
| } |
| |
| /** |
| * vpd_crete_eps - create an entry point structure |
| * |
| * @structure_table_len: structure table len |
| * @num_structures: number of structures in structure table |
| * |
| * As per SMBIOS spec, the caller must place this structure on a 16-byte |
| * boundary so that the anchor strings can be found. |
| * |
| * returns pointer to newly allocated entry point structure if successful |
| * returns NULL to indicate failure |
| * |
| * FIXME: This function needs to be more intelligent about parsing tables and |
| * obtaining information on its own. These arguments need to go away. |
| */ |
| struct vpd_entry *vpd_create_eps(uint16_t structure_table_len, |
| uint16_t num_structures, |
| uint32_t eps_base) { |
| struct vpd_entry *eps = NULL; |
| |
| /* size of structure only, no strings */ |
| eps = malloc(sizeof(struct vpd_entry)); |
| if (!eps) |
| return NULL; |
| memset(eps, 0, sizeof(*eps)); |
| |
| memcpy(eps->anchor_string, VPD_ENTRY_MAGIC, 4); |
| /* Note: entry point length should be 0x1F for v2.6 */ |
| eps->entry_length = sizeof(struct vpd_entry); |
| eps->major_ver = CONFIG_EPS_VPD_MAJOR_VERSION; |
| eps->minor_ver = CONFIG_EPS_VPD_MINOR_VERSION; |
| /* EPS revision based on version 2.1 or later */ |
| eps->entry_rev = 0; |
| /* note: nothing done with EPS formatted area */ |
| |
| /* Intermediate EPS (IEPS) stuff */ |
| memcpy(eps->inter_anchor_string, "_DMI_", 5); |
| |
| /* FIXME: implement vpd_table_length() and vpd_num_structures() */ |
| eps->table_length = structure_table_len; |
| |
| /* immediately follow the entry point structure */ |
| eps->table_address = eps_base + eps->entry_length; |
| |
| #ifdef CONFIG_EPS_NUM_STRUCTURES |
| eps->table_entry_count = CONFIG_EPS_NUM_STRUCTURES; |
| #else |
| eps->table_entry_count = num_structures; |
| #endif |
| eps->bcd_revision = (CONFIG_EPS_VPD_MAJOR_VERSION << 4) | |
| CONFIG_EPS_VPD_MINOR_VERSION; |
| |
| /* calculate IEPS checksum first, then the EPS checksum */ |
| eps->inter_anchor_cksum = zero8_csum(&eps->inter_anchor_string[0], 0xf); |
| eps->entry_cksum = zero8_csum((uint8_t *)eps, eps->entry_length); |
| |
| return eps; |
| } |
| |
| /** |
| * vpd_append_type127 - append type 127 (end of table) structure |
| * |
| * @handle: handle for this structure |
| * @buf: buffer to append to |
| * @len: length of buffer |
| * |
| * returns total size of newly re-sized buffer if successful |
| * returns <0 to indicate failure |
| */ |
| int vpd_append_type127(uint16_t handle, uint8_t **buf, size_t len) |
| { |
| struct vpd_table_eot *data; |
| size_t total_len, struct_len; |
| |
| struct_len = sizeof(struct vpd_table_eot) + 2; /* double terminator */ |
| total_len = len + struct_len; |
| *buf = realloc(*buf, total_len); |
| |
| data = (struct vpd_table_eot *)(*buf + len); |
| data->header.type = 127; |
| data->header.length = sizeof(*data); |
| data->header.handle = handle; |
| |
| memset(*buf + len + sizeof(*data), 0, 2); /* double terminator */ |
| |
| return total_len; |
| } |
| |
| /** |
| * vpd_append_type241 - append type 241 (binary blob pointer) structure |
| * |
| * @handle: handle for this structure |
| * @buf: buffer to append to |
| * @len: length of buffer |
| * @vendor: blob vendor string |
| * @desc: blob description string |
| * @variant: blob variant string |
| * |
| * returns total size of newly re-sized buffer if successful |
| * returns <0 to indicate failure |
| */ |
| int vpd_append_type241(uint16_t handle, uint8_t **buf, |
| size_t len, const char *uuid, uint32_t offset, |
| uint32_t size, const char *vendor, |
| const char *desc, const char *variant) |
| { |
| struct vpd_header *header; |
| struct vpd_table_binary_blob_pointer *data; |
| uint8_t *string_ptr; |
| size_t struct_len, total_len; |
| int string_index = 1; |
| |
| /* FIXME: Add sanity checking */ |
| struct_len = sizeof(struct vpd_header) + |
| sizeof(struct vpd_table_binary_blob_pointer); |
| if (vendor) |
| struct_len += strlen(vendor) + 1; |
| if (desc) |
| struct_len += strlen(desc) + 1; |
| if (variant) |
| struct_len += strlen(variant) + 1; |
| struct_len += 1; /* structure terminator */ |
| total_len = len + struct_len; |
| |
| *buf = realloc(*buf, total_len); |
| memset(*buf + len, 0, struct_len); |
| |
| header = (struct vpd_header *)(*buf + len); |
| data = (struct vpd_table_binary_blob_pointer *) |
| ((uint8_t *)header + sizeof(*header)); |
| string_ptr = (uint8_t *)data + sizeof(*data); |
| |
| /* fill in structure header details */ |
| header->type = VPD_TYPE_BINARY_BLOB_POINTER; |
| header->length = sizeof(*header) + sizeof(*data); |
| header->handle = handle; |
| |
| data->struct_major_version = 1; |
| data->struct_minor_version = 0; |
| |
| if (vendor) { |
| data->vendor = string_index; |
| string_index++; |
| sprintf(string_ptr, "%s%c", vendor, '\0'); |
| string_ptr += strlen(vendor) + 1; |
| } |
| |
| if (desc) { |
| data->description = string_index; |
| string_index++; |
| sprintf(string_ptr, "%s%c", desc, '\0'); |
| string_ptr += strlen(desc) + 1; |
| } |
| |
| data->major_version = 2; |
| data->minor_version = 0; |
| |
| if (variant) { |
| data->variant = string_index; |
| string_index++; |
| sprintf(string_ptr, "%s%c", variant, '\0'); |
| string_ptr += strlen(variant) + 1; |
| } |
| |
| memset(&data->reserved[0], 0, 5); |
| |
| if (uuid_parse(uuid, &data->uuid[0]) < 0) { |
| fprintf(stderr, "invalid UUID \"%s\" specified\n", uuid); |
| goto vpd_create_type241_fail; |
| } |
| |
| data->offset = offset; |
| data->size = size; |
| |
| return total_len; |
| |
| vpd_create_type241_fail: |
| return -1; |
| } |
| |
| /** |
| * vpd_type241_size - return the size of type 241 structure table. |
| * |
| * Type 241 structure contains 3 variant length of string at end of table. |
| * It is non-trival to get the length by sizeof(vpd_table_binary_blob_pointer). |
| * This function can help by adding 3 strlen(NULL-terminated string). |
| * |
| * @header: pointer to the start address of this structure table. |
| * |
| * returns total size of this structure table. |
| * returns <0 to indicate failure |
| */ |
| int vpd_type241_size(struct vpd_header *header) { |
| uint8_t *ptr = ((uint8_t*)header) + header->length; |
| int length = sizeof(struct vpd_header) + |
| sizeof(struct vpd_table_binary_blob_pointer); |
| int i; |
| |
| /* Sanity check */ |
| if (header->type != VPD_TYPE_BINARY_BLOB_POINTER) return -1; |
| |
| for (i = 0; i < 3; ++i) { |
| int len = strlen(ptr) + 1; |
| length += len; |
| ptr += len; |
| } |
| length++; /* Additional null(0) to indicate end of set. |
| * Refer to SMBIOS spec 3.1.3 Text Strings. */ |
| return length; |
| } |
| |
| void vpd_free_table(void *data) |
| { |
| uint8_t *foo = data; |
| |
| /* clean-up is trivially simple, for now... */ |
| free(foo); |
| } |