1/*
2 * SMBIOS Support
3 *
4 * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
5 *
6 * Authors:
7 *  Alex Williamson <alex.williamson@hp.com>
8 *
9 * This work is licensed under the terms of the GNU GPL, version 2.  See
10 * the COPYING file in the top-level directory.
11 *
12 */
13
14#include "sysemu.h"
15#include "smbios.h"
16
17/*
18 * Structures shared with the BIOS
19 */
20struct smbios_header {
21    uint16_t length;
22    uint8_t type;
23} __attribute__((__packed__));
24
25struct smbios_field {
26    struct smbios_header header;
27    uint8_t type;
28    uint16_t offset;
29    uint8_t data[];
30} __attribute__((__packed__));
31
32struct smbios_table {
33    struct smbios_header header;
34    uint8_t data[];
35} __attribute__((__packed__));
36
37#define SMBIOS_FIELD_ENTRY 0
38#define SMBIOS_TABLE_ENTRY 1
39
40
41static uint8_t *smbios_entries;
42static size_t smbios_entries_len;
43
44uint8_t *smbios_get_table(size_t *length)
45{
46    *length = smbios_entries_len;
47    return smbios_entries;
48}
49
50/*
51 * To avoid unresolvable overlaps in data, don't allow both
52 * tables and fields for the same smbios type.
53 */
54static void smbios_check_collision(int type, int entry)
55{
56    uint16_t *num_entries = (uint16_t *)smbios_entries;
57    struct smbios_header *header;
58    char *p;
59    int i;
60
61    if (!num_entries)
62        return;
63
64    p = (char *)(num_entries + 1);
65
66    for (i = 0; i < *num_entries; i++) {
67        header = (struct smbios_header *)p;
68        if (entry == SMBIOS_TABLE_ENTRY && header->type == SMBIOS_FIELD_ENTRY) {
69            struct smbios_field *field = (void *)header;
70            if (type == field->type) {
71                fprintf(stderr, "SMBIOS type %d field already defined, "
72                                "cannot add table\n", type);
73                exit(1);
74            }
75        } else if (entry == SMBIOS_FIELD_ENTRY &&
76                   header->type == SMBIOS_TABLE_ENTRY) {
77            struct smbios_structure_header *table = (void *)(header + 1);
78            if (type == table->type) {
79                fprintf(stderr, "SMBIOS type %d table already defined, "
80                                "cannot add field\n", type);
81                exit(1);
82            }
83        }
84        p += le16_to_cpu(header->length);
85    }
86}
87
88void smbios_add_field(int type, int offset, int len, void *data)
89{
90    struct smbios_field *field;
91
92    smbios_check_collision(type, SMBIOS_FIELD_ENTRY);
93
94    if (!smbios_entries) {
95        smbios_entries_len = sizeof(uint16_t);
96        smbios_entries = qemu_mallocz(smbios_entries_len);
97    }
98    smbios_entries = qemu_realloc(smbios_entries, smbios_entries_len +
99                                                  sizeof(*field) + len);
100    field = (struct smbios_field *)(smbios_entries + smbios_entries_len);
101    field->header.type = SMBIOS_FIELD_ENTRY;
102    field->header.length = cpu_to_le16(sizeof(*field) + len);
103
104    field->type = type;
105    field->offset = cpu_to_le16(offset);
106    memcpy(field->data, data, len);
107
108    smbios_entries_len += sizeof(*field) + len;
109    (*(uint16_t *)smbios_entries) =
110            cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1);
111}
112
113static void smbios_build_type_0_fields(const char *t)
114{
115    char buf[1024];
116
117    if (get_param_value(buf, sizeof(buf), "vendor", t))
118        smbios_add_field(0, offsetof(struct smbios_type_0, vendor_str),
119                         strlen(buf) + 1, buf);
120    if (get_param_value(buf, sizeof(buf), "version", t))
121        smbios_add_field(0, offsetof(struct smbios_type_0, bios_version_str),
122                         strlen(buf) + 1, buf);
123    if (get_param_value(buf, sizeof(buf), "date", t))
124        smbios_add_field(0, offsetof(struct smbios_type_0,
125                                     bios_release_date_str),
126                                     strlen(buf) + 1, buf);
127    if (get_param_value(buf, sizeof(buf), "release", t)) {
128        int major, minor;
129        sscanf(buf, "%d.%d", &major, &minor);
130        smbios_add_field(0, offsetof(struct smbios_type_0,
131                                     system_bios_major_release), 1, &major);
132        smbios_add_field(0, offsetof(struct smbios_type_0,
133                                     system_bios_minor_release), 1, &minor);
134    }
135}
136
137static void smbios_build_type_1_fields(const char *t)
138{
139    char buf[1024];
140
141    if (get_param_value(buf, sizeof(buf), "manufacturer", t))
142        smbios_add_field(1, offsetof(struct smbios_type_1, manufacturer_str),
143                         strlen(buf) + 1, buf);
144    if (get_param_value(buf, sizeof(buf), "product", t))
145        smbios_add_field(1, offsetof(struct smbios_type_1, product_name_str),
146                         strlen(buf) + 1, buf);
147    if (get_param_value(buf, sizeof(buf), "version", t))
148        smbios_add_field(1, offsetof(struct smbios_type_1, version_str),
149                         strlen(buf) + 1, buf);
150    if (get_param_value(buf, sizeof(buf), "serial", t))
151        smbios_add_field(1, offsetof(struct smbios_type_1, serial_number_str),
152                         strlen(buf) + 1, buf);
153    if (get_param_value(buf, sizeof(buf), "uuid", t)) {
154        if (qemu_uuid_parse(buf, qemu_uuid) != 0) {
155            fprintf(stderr, "Invalid SMBIOS UUID string\n");
156            exit(1);
157        }
158    }
159    if (get_param_value(buf, sizeof(buf), "sku", t))
160        smbios_add_field(1, offsetof(struct smbios_type_1, sku_number_str),
161                         strlen(buf) + 1, buf);
162    if (get_param_value(buf, sizeof(buf), "family", t))
163        smbios_add_field(1, offsetof(struct smbios_type_1, family_str),
164                         strlen(buf) + 1, buf);
165}
166
167int smbios_entry_add(const char *t)
168{
169    char buf[1024];
170
171    if (get_param_value(buf, sizeof(buf), "file", t)) {
172        struct smbios_structure_header *header;
173        struct smbios_table *table;
174        int size = get_image_size(buf);
175
176        if (size < sizeof(struct smbios_structure_header)) {
177            fprintf(stderr, "Cannot read smbios file %s", buf);
178            exit(1);
179        }
180
181        if (!smbios_entries) {
182            smbios_entries_len = sizeof(uint16_t);
183            smbios_entries = qemu_mallocz(smbios_entries_len);
184        }
185
186        smbios_entries = qemu_realloc(smbios_entries, smbios_entries_len +
187                                                      sizeof(*table) + size);
188        table = (struct smbios_table *)(smbios_entries + smbios_entries_len);
189        table->header.type = SMBIOS_TABLE_ENTRY;
190        table->header.length = cpu_to_le16(sizeof(*table) + size);
191
192        if (load_image(buf, table->data) != size) {
193            fprintf(stderr, "Failed to load smbios file %s", buf);
194            exit(1);
195        }
196
197        header = (struct smbios_structure_header *)(table->data);
198        smbios_check_collision(header->type, SMBIOS_TABLE_ENTRY);
199
200        smbios_entries_len += sizeof(*table) + size;
201        (*(uint16_t *)smbios_entries) =
202                cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1);
203        return 0;
204    }
205
206    if (get_param_value(buf, sizeof(buf), "type", t)) {
207        unsigned long type = strtoul(buf, NULL, 0);
208        switch (type) {
209        case 0:
210            smbios_build_type_0_fields(t);
211            return 0;
212        case 1:
213            smbios_build_type_1_fields(t);
214            return 0;
215        default:
216            fprintf(stderr, "Don't know how to build fields for SMBIOS type "
217                    "%ld\n", type);
218            exit(1);
219        }
220    }
221
222    fprintf(stderr, "smbios: must specify type= or file=\n");
223    return -1;
224}
225