diff options
Diffstat (limited to 'jslinux-2019-12-21/tinyemu-2019-12-21/machine.c')
| -rw-r--r-- | jslinux-2019-12-21/tinyemu-2019-12-21/machine.c | 640 |
1 files changed, 640 insertions, 0 deletions
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/machine.c b/jslinux-2019-12-21/tinyemu-2019-12-21/machine.c new file mode 100644 index 0000000..09c7940 --- /dev/null +++ b/jslinux-2019-12-21/tinyemu-2019-12-21/machine.c @@ -0,0 +1,640 @@ +/* + * VM utilities + * + * Copyright (c) 2017 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <inttypes.h> +#include <assert.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> + +#include "cutils.h" +#include "iomem.h" +#include "virtio.h" +#include "machine.h" +#include "fs_utils.h" +#ifdef CONFIG_FS_NET +#include "fs_wget.h" +#endif + +void __attribute__((format(printf, 1, 2))) vm_error(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); +#ifdef EMSCRIPTEN + vprintf(fmt, ap); +#else + vfprintf(stderr, fmt, ap); +#endif + va_end(ap); +} + +int vm_get_int(JSONValue obj, const char *name, int *pval) +{ + JSONValue val; + val = json_object_get(obj, name); + if (json_is_undefined(val)) { + vm_error("expecting '%s' property\n", name); + return -1; + } + if (val.type != JSON_INT) { + vm_error("%s: integer expected\n", name); + return -1; + } + *pval = val.u.int32; + return 0; +} + +int vm_get_int_opt(JSONValue obj, const char *name, int *pval, int def_val) +{ + JSONValue val; + val = json_object_get(obj, name); + if (json_is_undefined(val)) { + *pval = def_val; + return 0; + } + if (val.type != JSON_INT) { + vm_error("%s: integer expected\n", name); + return -1; + } + *pval = val.u.int32; + return 0; +} + +static int vm_get_str2(JSONValue obj, const char *name, const char **pstr, + BOOL is_opt) +{ + JSONValue val; + val = json_object_get(obj, name); + if (json_is_undefined(val)) { + if (is_opt) { + *pstr = NULL; + return 0; + } else { + vm_error("expecting '%s' property\n", name); + return -1; + } + } + if (val.type != JSON_STR) { + vm_error("%s: string expected\n", name); + return -1; + } + *pstr = val.u.str->data; + return 0; +} + +static int vm_get_str(JSONValue obj, const char *name, const char **pstr) +{ + return vm_get_str2(obj, name, pstr, FALSE); +} + +static int vm_get_str_opt(JSONValue obj, const char *name, const char **pstr) +{ + return vm_get_str2(obj, name, pstr, TRUE); +} + +static char *strdup_null(const char *str) +{ + if (!str) + return NULL; + else + return strdup(str); +} + +/* currently only for "TZ" */ +static char *cmdline_subst(const char *cmdline) +{ + DynBuf dbuf; + const char *p; + char var_name[32], *q, buf[32]; + + dbuf_init(&dbuf); + p = cmdline; + while (*p != '\0') { + if (p[0] == '$' && p[1] == '{') { + p += 2; + q = var_name; + while (*p != '\0' && *p != '}') { + if ((q - var_name) < sizeof(var_name) - 1) + *q++ = *p; + p++; + } + *q = '\0'; + if (*p == '}') + p++; + if (!strcmp(var_name, "TZ")) { + time_t ti; + struct tm tm; + int n, sg; + /* get the offset to UTC */ + time(&ti); + localtime_r(&ti, &tm); + n = tm.tm_gmtoff / 60; + sg = '-'; + if (n < 0) { + sg = '+'; + n = -n; + } + snprintf(buf, sizeof(buf), "UTC%c%02d:%02d", + sg, n / 60, n % 60); + dbuf_putstr(&dbuf, buf); + } + } else { + dbuf_putc(&dbuf, *p++); + } + } + dbuf_putc(&dbuf, 0); + return (char *)dbuf.buf; +} + +static BOOL find_name(const char *name, const char *name_list) +{ + size_t len; + const char *p, *r; + + p = name_list; + for(;;) { + r = strchr(p, ','); + if (!r) { + if (!strcmp(name, p)) + return TRUE; + break; + } else { + len = r - p; + if (len == strlen(name) && !memcmp(name, p, len)) + return TRUE; + p = r + 1; + } + } + return FALSE; +} + +static const VirtMachineClass *virt_machine_list[] = { +#if defined(EMSCRIPTEN) + /* only a single machine in the EMSCRIPTEN target */ +#ifndef CONFIG_X86EMU + &riscv_machine_class, +#endif +#else + &riscv_machine_class, +#endif /* !EMSCRIPTEN */ +#ifdef CONFIG_X86EMU + &pc_machine_class, +#endif + NULL, +}; + +static const VirtMachineClass *virt_machine_find_class(const char *machine_name) +{ + const VirtMachineClass *vmc, **pvmc; + + for(pvmc = virt_machine_list; *pvmc != NULL; pvmc++) { + vmc = *pvmc; + if (find_name(machine_name, vmc->machine_names)) + return vmc; + } + return NULL; +} + +static int virt_machine_parse_config(VirtMachineParams *p, + char *config_file_str, int len) +{ + int version, val; + const char *tag_name, *str; + char buf1[256]; + JSONValue cfg, obj, el; + + cfg = json_parse_value_len(config_file_str, len); + if (json_is_error(cfg)) { + vm_error("error: %s\n", json_get_error(cfg)); + json_free(cfg); + return -1; + } + + if (vm_get_int(cfg, "version", &version) < 0) + goto tag_fail; + if (version != VM_CONFIG_VERSION) { + if (version > VM_CONFIG_VERSION) { + vm_error("The emulator is too old to run this VM: please upgrade\n"); + return -1; + } else { + vm_error("The VM configuration file is too old for this emulator version: please upgrade the VM configuration file\n"); + return -1; + } + } + + if (vm_get_str(cfg, "machine", &str) < 0) + goto tag_fail; + p->machine_name = strdup(str); + p->vmc = virt_machine_find_class(p->machine_name); + if (!p->vmc) { + vm_error("Unknown machine name: %s\n", p->machine_name); + goto tag_fail; + } + p->vmc->virt_machine_set_defaults(p); + + tag_name = "memory_size"; + if (vm_get_int(cfg, tag_name, &val) < 0) + goto tag_fail; + p->ram_size = (uint64_t)val << 20; + + tag_name = "bios"; + if (vm_get_str_opt(cfg, tag_name, &str) < 0) + goto tag_fail; + if (str) { + p->files[VM_FILE_BIOS].filename = strdup(str); + } + + tag_name = "kernel"; + if (vm_get_str_opt(cfg, tag_name, &str) < 0) + goto tag_fail; + if (str) { + p->files[VM_FILE_KERNEL].filename = strdup(str); + } + + tag_name = "initrd"; + if (vm_get_str_opt(cfg, tag_name, &str) < 0) + goto tag_fail; + if (str) { + p->files[VM_FILE_INITRD].filename = strdup(str); + } + + if (vm_get_str_opt(cfg, "cmdline", &str) < 0) + goto tag_fail; + if (str) { + p->cmdline = cmdline_subst(str); + } + + for(;;) { + snprintf(buf1, sizeof(buf1), "drive%d", p->drive_count); + obj = json_object_get(cfg, buf1); + if (json_is_undefined(obj)) + break; + if (p->drive_count >= MAX_DRIVE_DEVICE) { + vm_error("Too many drives\n"); + return -1; + } + if (vm_get_str(obj, "file", &str) < 0) + goto tag_fail; + p->tab_drive[p->drive_count].filename = strdup(str); + if (vm_get_str_opt(obj, "device", &str) < 0) + goto tag_fail; + p->tab_drive[p->drive_count].device = strdup_null(str); + p->drive_count++; + } + + for(;;) { + snprintf(buf1, sizeof(buf1), "fs%d", p->fs_count); + obj = json_object_get(cfg, buf1); + if (json_is_undefined(obj)) + break; + if (p->fs_count >= MAX_DRIVE_DEVICE) { + vm_error("Too many filesystems\n"); + return -1; + } + if (vm_get_str(obj, "file", &str) < 0) + goto tag_fail; + p->tab_fs[p->fs_count].filename = strdup(str); + if (vm_get_str_opt(obj, "tag", &str) < 0) + goto tag_fail; + if (!str) { + if (p->fs_count == 0) + strcpy(buf1, "/dev/root"); + else + snprintf(buf1, sizeof(buf1), "/dev/root%d", p->fs_count); + str = buf1; + } + p->tab_fs[p->fs_count].tag = strdup(str); + p->fs_count++; + } + + for(;;) { + snprintf(buf1, sizeof(buf1), "eth%d", p->eth_count); + obj = json_object_get(cfg, buf1); + if (json_is_undefined(obj)) + break; + if (p->eth_count >= MAX_ETH_DEVICE) { + vm_error("Too many ethernet interfaces\n"); + return -1; + } + if (vm_get_str(obj, "driver", &str) < 0) + goto tag_fail; + p->tab_eth[p->eth_count].driver = strdup(str); + if (!strcmp(str, "tap")) { + if (vm_get_str(obj, "ifname", &str) < 0) + goto tag_fail; + p->tab_eth[p->eth_count].ifname = strdup(str); + } + p->eth_count++; + } + + p->display_device = NULL; + obj = json_object_get(cfg, "display0"); + if (!json_is_undefined(obj)) { + if (vm_get_str(obj, "device", &str) < 0) + goto tag_fail; + p->display_device = strdup(str); + if (vm_get_int(obj, "width", &p->width) < 0) + goto tag_fail; + if (vm_get_int(obj, "height", &p->height) < 0) + goto tag_fail; + if (vm_get_str_opt(obj, "vga_bios", &str) < 0) + goto tag_fail; + if (str) { + p->files[VM_FILE_VGA_BIOS].filename = strdup(str); + } + } + + if (vm_get_str_opt(cfg, "input_device", &str) < 0) + goto tag_fail; + p->input_device = strdup_null(str); + + if (vm_get_str_opt(cfg, "accel", &str) < 0) + goto tag_fail; + if (str) { + if (!strcmp(str, "none")) { + p->accel_enable = FALSE; + } else if (!strcmp(str, "auto")) { + p->accel_enable = TRUE; + } else { + vm_error("unsupported 'accel' config: %s\n", str); + return -1; + } + } + + tag_name = "rtc_local_time"; + el = json_object_get(cfg, tag_name); + if (!json_is_undefined(el)) { + if (el.type != JSON_BOOL) { + vm_error("%s: boolean expected\n", tag_name); + goto tag_fail; + } + p->rtc_local_time = el.u.b; + } + + json_free(cfg); + return 0; + tag_fail: + json_free(cfg); + return -1; +} + +typedef void FSLoadFileCB(void *opaque, uint8_t *buf, int buf_len); + +typedef struct { + VirtMachineParams *vm_params; + void (*start_cb)(void *opaque); + void *opaque; + + FSLoadFileCB *file_load_cb; + void *file_load_opaque; + int file_index; +} VMConfigLoadState; + +static void config_file_loaded(void *opaque, uint8_t *buf, int buf_len); +static void config_additional_file_load(VMConfigLoadState *s); +static void config_additional_file_load_cb(void *opaque, + uint8_t *buf, int buf_len); + +/* XXX: win32, URL */ +char *get_file_path(const char *base_filename, const char *filename) +{ + int len, len1; + char *fname, *p; + + if (!base_filename) + goto done; + if (strchr(filename, ':')) + goto done; /* full URL */ + if (filename[0] == '/') + goto done; + p = strrchr(base_filename, '/'); + if (!p) { + done: + return strdup(filename); + } + len = p + 1 - base_filename; + len1 = strlen(filename); + fname = malloc(len + len1 + 1); + memcpy(fname, base_filename, len); + memcpy(fname + len, filename, len1 + 1); + return fname; +} + + +#ifdef EMSCRIPTEN +static int load_file(uint8_t **pbuf, const char *filename) +{ + abort(); +} +#else +/* return -1 if error. */ +static int load_file(uint8_t **pbuf, const char *filename) +{ + FILE *f; + int size; + uint8_t *buf; + + f = fopen(filename, "rb"); + if (!f) { + perror(filename); + exit(1); + } + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, 0, SEEK_SET); + buf = malloc(size); + if (fread(buf, 1, size, f) != size) { + fprintf(stderr, "%s: read error\n", filename); + exit(1); + } + fclose(f); + *pbuf = buf; + return size; +} +#endif + +#ifdef CONFIG_FS_NET +static void config_load_file_cb(void *opaque, int err, void *data, size_t size) +{ + VMConfigLoadState *s = opaque; + + // printf("err=%d data=%p size=%ld\n", err, data, size); + if (err < 0) { + vm_error("Error %d while loading file\n", -err); + exit(1); + } + s->file_load_cb(s->file_load_opaque, data, size); +} +#endif + +static void config_load_file(VMConfigLoadState *s, const char *filename, + FSLoadFileCB *cb, void *opaque) +{ + // printf("loading %s\n", filename); +#ifdef CONFIG_FS_NET + if (is_url(filename)) { + s->file_load_cb = cb; + s->file_load_opaque = opaque; + fs_wget(filename, NULL, NULL, s, config_load_file_cb, TRUE); + } else +#endif + { + uint8_t *buf; + int size; + size = load_file(&buf, filename); + cb(opaque, buf, size); + free(buf); + } +} + +void virt_machine_load_config_file(VirtMachineParams *p, + const char *filename, + void (*start_cb)(void *opaque), + void *opaque) +{ + VMConfigLoadState *s; + + s = mallocz(sizeof(*s)); + s->vm_params = p; + s->start_cb = start_cb; + s->opaque = opaque; + p->cfg_filename = strdup(filename); + + config_load_file(s, filename, config_file_loaded, s); +} + +static void config_file_loaded(void *opaque, uint8_t *buf, int buf_len) +{ + VMConfigLoadState *s = opaque; + VirtMachineParams *p = s->vm_params; + + if (virt_machine_parse_config(p, (char *)buf, buf_len) < 0) + exit(1); + + /* load the additional files */ + s->file_index = 0; + config_additional_file_load(s); +} + +static void config_additional_file_load(VMConfigLoadState *s) +{ + VirtMachineParams *p = s->vm_params; + while (s->file_index < VM_FILE_COUNT && + p->files[s->file_index].filename == NULL) { + s->file_index++; + } + if (s->file_index == VM_FILE_COUNT) { + if (s->start_cb) + s->start_cb(s->opaque); + free(s); + } else { + char *fname; + + fname = get_file_path(p->cfg_filename, + p->files[s->file_index].filename); + config_load_file(s, fname, + config_additional_file_load_cb, s); + free(fname); + } +} + +static void config_additional_file_load_cb(void *opaque, + uint8_t *buf, int buf_len) +{ + VMConfigLoadState *s = opaque; + VirtMachineParams *p = s->vm_params; + + p->files[s->file_index].buf = malloc(buf_len); + memcpy(p->files[s->file_index].buf, buf, buf_len); + p->files[s->file_index].len = buf_len; + + /* load the next files */ + s->file_index++; + config_additional_file_load(s); +} + +void vm_add_cmdline(VirtMachineParams *p, const char *cmdline) +{ + char *new_cmdline, *old_cmdline; + if (cmdline[0] == '!') { + new_cmdline = strdup(cmdline + 1); + } else { + old_cmdline = p->cmdline; + if (!old_cmdline) + old_cmdline = ""; + new_cmdline = malloc(strlen(old_cmdline) + 1 + strlen(cmdline) + 1); + strcpy(new_cmdline, old_cmdline); + strcat(new_cmdline, " "); + strcat(new_cmdline, cmdline); + } + free(p->cmdline); + p->cmdline = new_cmdline; +} + +void virt_machine_free_config(VirtMachineParams *p) +{ + int i; + + free(p->machine_name); + free(p->cmdline); + for(i = 0; i < VM_FILE_COUNT; i++) { + free(p->files[i].filename); + free(p->files[i].buf); + } + for(i = 0; i < p->drive_count; i++) { + free(p->tab_drive[i].filename); + free(p->tab_drive[i].device); + } + for(i = 0; i < p->fs_count; i++) { + free(p->tab_fs[i].filename); + free(p->tab_fs[i].tag); + } + for(i = 0; i < p->eth_count; i++) { + free(p->tab_eth[i].driver); + free(p->tab_eth[i].ifname); + } + free(p->input_device); + free(p->display_device); + free(p->cfg_filename); +} + +VirtMachine *virt_machine_init(const VirtMachineParams *p) +{ + const VirtMachineClass *vmc = p->vmc; + return vmc->virt_machine_init(p); +} + +void virt_machine_set_defaults(VirtMachineParams *p) +{ + memset(p, 0, sizeof(*p)); +} + +void virt_machine_end(VirtMachine *s) +{ + s->vmc->virt_machine_end(s); +} |
