aboutsummaryrefslogtreecommitdiff
path: root/jslinux-2019-12-21/tinyemu-2019-12-21/machine.c
diff options
context:
space:
mode:
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.c640
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);
+}