diff options
Diffstat (limited to 'hw/smbios/smbios.c')
-rw-r--r-- | hw/smbios/smbios.c | 85 |
1 files changed, 72 insertions, 13 deletions
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index 7cc950b41c..8b30906e50 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -110,7 +110,7 @@ static struct { static struct { size_t nvalues; - const char **values; + char **values; } type11; static struct { @@ -314,6 +314,11 @@ static const QemuOptDesc qemu_smbios_type11_opts[] = { .type = QEMU_OPT_STRING, .help = "OEM string data", }, + { + .name = "path", + .type = QEMU_OPT_STRING, + .help = "OEM string data from file", + }, }; static const QemuOptDesc qemu_smbios_type17_opts[] = { @@ -360,6 +365,13 @@ static void smbios_register_config(void) opts_init(smbios_register_config); +/* + * The SMBIOS 2.1 "structure table length" field in the + * entry point uses a 16-bit integer, so we're limited + * in total table size + */ +#define SMBIOS_21_MAX_TABLES_LEN 0xffff + static void smbios_validate_table(MachineState *ms) { uint32_t expect_t4_count = smbios_legacy ? @@ -370,6 +382,13 @@ static void smbios_validate_table(MachineState *ms) expect_t4_count, smbios_type4_count); exit(1); } + + if (smbios_ep_type == SMBIOS_ENTRY_POINT_21 && + smbios_tables_len > SMBIOS_21_MAX_TABLES_LEN) { + error_report("SMBIOS 2.1 table length %zu exceeds %d", + smbios_tables_len, SMBIOS_21_MAX_TABLES_LEN); + exit(1); + } } @@ -641,6 +660,8 @@ static void smbios_build_type_11_table(void) for (i = 0; i < type11.nvalues; i++) { SMBIOS_TABLE_SET_STR_LIST(11, type11.values[i]); + g_free(type11.values[i]); + type11.values[i] = NULL; } SMBIOS_BUILD_TABLE_POST; @@ -940,9 +961,8 @@ static void save_opt(const char **dest, QemuOpts *opts, const char *name) struct opt_list { - const char *name; size_t *ndest; - const char ***dest; + char ***dest; }; static int save_opt_one(void *opaque, @@ -951,23 +971,60 @@ static int save_opt_one(void *opaque, { struct opt_list *opt = opaque; - if (!g_str_equal(name, opt->name)) { - return 0; + if (g_str_equal(name, "path")) { + g_autoptr(GByteArray) data = g_byte_array_new(); + g_autofree char *buf = g_new(char, 4096); + ssize_t ret; + int fd = qemu_open(value, O_RDONLY, errp); + if (fd < 0) { + return -1; + } + + while (1) { + ret = read(fd, buf, 4096); + if (ret == 0) { + break; + } + if (ret < 0) { + error_setg(errp, "Unable to read from %s: %s", + value, strerror(errno)); + return -1; + } + if (memchr(buf, '\0', ret)) { + error_setg(errp, "NUL in OEM strings value in %s", value); + return -1; + } + g_byte_array_append(data, (guint8 *)buf, ret); + } + + close(fd); + + *opt->dest = g_renew(char *, *opt->dest, (*opt->ndest) + 1); + (*opt->dest)[*opt->ndest] = (char *)g_byte_array_free(data, FALSE); + (*opt->ndest)++; + data = NULL; + } else if (g_str_equal(name, "value")) { + *opt->dest = g_renew(char *, *opt->dest, (*opt->ndest) + 1); + (*opt->dest)[*opt->ndest] = g_strdup(value); + (*opt->ndest)++; + } else if (!g_str_equal(name, "type")) { + error_setg(errp, "Unexpected option %s", name); + return -1; } - *opt->dest = g_renew(const char *, *opt->dest, (*opt->ndest) + 1); - (*opt->dest)[*opt->ndest] = value; - (*opt->ndest)++; return 0; } -static void save_opt_list(size_t *ndest, const char ***dest, - QemuOpts *opts, const char *name) +static bool save_opt_list(size_t *ndest, char ***dest, QemuOpts *opts, + Error **errp) { struct opt_list opt = { - name, ndest, dest, + ndest, dest, }; - qemu_opt_foreach(opts, save_opt_one, &opt, NULL); + if (!qemu_opt_foreach(opts, save_opt_one, &opt, errp)) { + return false; + } + return true; } void smbios_entry_add(QemuOpts *opts, Error **errp) @@ -1149,7 +1206,9 @@ void smbios_entry_add(QemuOpts *opts, Error **errp) if (!qemu_opts_validate(opts, qemu_smbios_type11_opts, errp)) { return; } - save_opt_list(&type11.nvalues, &type11.values, opts, "value"); + if (!save_opt_list(&type11.nvalues, &type11.values, opts, errp)) { + return; + } return; case 17: if (!qemu_opts_validate(opts, qemu_smbios_type17_opts, errp)) { |