aboutsummaryrefslogtreecommitdiff
path: root/jslinux-2019-12-21/tinyemu-2019-12-21/jsemu.c
diff options
context:
space:
mode:
Diffstat (limited to 'jslinux-2019-12-21/tinyemu-2019-12-21/jsemu.c')
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/jsemu.c349
1 files changed, 349 insertions, 0 deletions
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/jsemu.c b/jslinux-2019-12-21/tinyemu-2019-12-21/jsemu.c
new file mode 100644
index 0000000..3641db4
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/jsemu.c
@@ -0,0 +1,349 @@
+/*
+ * JS emulator main
+ *
+ * Copyright (c) 2016-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 <emscripten.h>
+
+#include "cutils.h"
+#include "iomem.h"
+#include "virtio.h"
+#include "machine.h"
+#include "list.h"
+#include "fbuf.h"
+
+void virt_machine_run(void *opaque);
+
+/* provided in lib.js */
+extern void console_write(void *opaque, const uint8_t *buf, int len);
+extern void console_get_size(int *pw, int *ph);
+extern void fb_refresh(void *opaque, void *data,
+ int x, int y, int w, int h, int stride);
+extern void net_recv_packet(EthernetDevice *bs,
+ const uint8_t *buf, int len);
+
+static uint8_t console_fifo[1024];
+static int console_fifo_windex;
+static int console_fifo_rindex;
+static int console_fifo_count;
+static BOOL console_resize_pending;
+
+static int global_width;
+static int global_height;
+static VirtMachine *global_vm;
+static BOOL global_carrier_state;
+
+static int console_read(void *opaque, uint8_t *buf, int len)
+{
+ int out_len, l;
+ len = min_int(len, console_fifo_count);
+ console_fifo_count -= len;
+ out_len = 0;
+ while (len != 0) {
+ l = min_int(len, sizeof(console_fifo) - console_fifo_rindex);
+ memcpy(buf + out_len, console_fifo + console_fifo_rindex, l);
+ len -= l;
+ out_len += l;
+ console_fifo_rindex += l;
+ if (console_fifo_rindex == sizeof(console_fifo))
+ console_fifo_rindex = 0;
+ }
+ return out_len;
+}
+
+/* called from JS */
+void console_queue_char(int c)
+{
+ if (console_fifo_count < sizeof(console_fifo)) {
+ console_fifo[console_fifo_windex] = c;
+ if (++console_fifo_windex == sizeof(console_fifo))
+ console_fifo_windex = 0;
+ console_fifo_count++;
+ }
+}
+
+/* called from JS */
+void display_key_event(int is_down, int key_code)
+{
+ if (global_vm) {
+ vm_send_key_event(global_vm, is_down, key_code);
+ }
+}
+
+/* called from JS */
+static int mouse_last_x, mouse_last_y, mouse_last_buttons;
+
+void display_mouse_event(int dx, int dy, int buttons)
+{
+ if (global_vm) {
+ if (vm_mouse_is_absolute(global_vm) || 1) {
+ dx = min_int(dx, global_width - 1);
+ dy = min_int(dy, global_height - 1);
+ dx = (dx * VIRTIO_INPUT_ABS_SCALE) / global_width;
+ dy = (dy * VIRTIO_INPUT_ABS_SCALE) / global_height;
+ } else {
+ /* relative mouse is not supported */
+ dx = 0;
+ dy = 0;
+ }
+ mouse_last_x = dx;
+ mouse_last_y = dy;
+ mouse_last_buttons = buttons;
+ vm_send_mouse_event(global_vm, dx, dy, 0, buttons);
+ }
+}
+
+/* called from JS */
+void display_wheel_event(int dz)
+{
+ if (global_vm) {
+ vm_send_mouse_event(global_vm, mouse_last_x, mouse_last_y, dz,
+ mouse_last_buttons);
+ }
+}
+
+/* called from JS */
+void net_write_packet(const uint8_t *buf, int buf_len)
+{
+ EthernetDevice *net = global_vm->net;
+ if (net) {
+ net->device_write_packet(net, buf, buf_len);
+ }
+}
+
+/* called from JS */
+void net_set_carrier(BOOL carrier_state)
+{
+ EthernetDevice *net;
+ global_carrier_state = carrier_state;
+ if (global_vm && global_vm->net) {
+ net = global_vm->net;
+ net->device_set_carrier(net, carrier_state);
+ }
+}
+
+static void fb_refresh1(FBDevice *fb_dev, void *opaque,
+ int x, int y, int w, int h)
+{
+ int stride = fb_dev->stride;
+ fb_refresh(opaque, fb_dev->fb_data + y * stride + x * 4, x, y, w, h,
+ stride);
+}
+
+static CharacterDevice *console_init(void)
+{
+ CharacterDevice *dev;
+ console_resize_pending = TRUE;
+ dev = mallocz(sizeof(*dev));
+ dev->write_data = console_write;
+ dev->read_data = console_read;
+ return dev;
+}
+
+typedef struct {
+ VirtMachineParams *p;
+ int ram_size;
+ char *cmdline;
+ BOOL has_network;
+ char *pwd;
+} VMStartState;
+
+static void init_vm(void *arg);
+static void init_vm_fs(void *arg);
+static void init_vm_drive(void *arg);
+
+void vm_start(const char *url, int ram_size, const char *cmdline,
+ const char *pwd, int width, int height, BOOL has_network)
+{
+ VMStartState *s;
+
+ s = mallocz(sizeof(*s));
+ s->ram_size = ram_size;
+ s->cmdline = strdup(cmdline);
+ if (pwd)
+ s->pwd = strdup(pwd);
+ global_width = width;
+ global_height = height;
+ s->has_network = has_network;
+ s->p = mallocz(sizeof(VirtMachineParams));
+ virt_machine_set_defaults(s->p);
+ virt_machine_load_config_file(s->p, url, init_vm_fs, s);
+}
+
+static void init_vm_fs(void *arg)
+{
+ VMStartState *s = arg;
+ VirtMachineParams *p = s->p;
+
+ if (p->fs_count > 0) {
+ assert(p->fs_count == 1);
+ p->tab_fs[0].fs_dev = fs_net_init(p->tab_fs[0].filename,
+ init_vm_drive, s);
+ if (s->pwd) {
+ fs_net_set_pwd(p->tab_fs[0].fs_dev, s->pwd);
+ }
+ } else {
+ init_vm_drive(s);
+ }
+}
+
+static void init_vm_drive(void *arg)
+{
+ VMStartState *s = arg;
+ VirtMachineParams *p = s->p;
+
+ if (p->drive_count > 0) {
+ assert(p->drive_count == 1);
+ p->tab_drive[0].block_dev =
+ block_device_init_http(p->tab_drive[0].filename,
+ 131072,
+ init_vm, s);
+ } else {
+ init_vm(s);
+ }
+}
+
+static void init_vm(void *arg)
+{
+ VMStartState *s = arg;
+ VirtMachine *m;
+ VirtMachineParams *p = s->p;
+ int i;
+
+ p->rtc_real_time = TRUE;
+ p->ram_size = s->ram_size << 20;
+ if (s->cmdline && s->cmdline[0] != '\0') {
+ vm_add_cmdline(s->p, s->cmdline);
+ }
+
+ if (global_width > 0 && global_height > 0) {
+ /* enable graphic output if needed */
+ if (!p->display_device)
+ p->display_device = strdup("simplefb");
+ p->width = global_width;
+ p->height = global_height;
+ } else {
+ p->console = console_init();
+ }
+
+ if (p->eth_count > 0 && !s->has_network) {
+ /* remove the interfaces */
+ for(i = 0; i < p->eth_count; i++) {
+ free(p->tab_eth[i].ifname);
+ free(p->tab_eth[i].driver);
+ }
+ p->eth_count = 0;
+ }
+
+ if (p->eth_count > 0) {
+ EthernetDevice *net;
+ int i;
+ assert(p->eth_count == 1);
+ net = mallocz(sizeof(EthernetDevice));
+ net->mac_addr[0] = 0x02;
+ for(i = 1; i < 6; i++)
+ net->mac_addr[i] = (int)(emscripten_random() * 256);
+ net->write_packet = net_recv_packet;
+ net->opaque = NULL;
+ p->tab_eth[0].net = net;
+ }
+
+ m = virt_machine_init(p);
+ global_vm = m;
+
+ virt_machine_free_config(s->p);
+
+ if (m->net) {
+ m->net->device_set_carrier(m->net, global_carrier_state);
+ }
+
+ free(s->p);
+ free(s->cmdline);
+ if (s->pwd) {
+ memset(s->pwd, 0, strlen(s->pwd));
+ free(s->pwd);
+ }
+ free(s);
+
+ emscripten_async_call(virt_machine_run, m, 0);
+}
+
+/* need to be long enough to hide the non zero delay of setTimeout(_, 0) */
+#define MAX_EXEC_TOTAL_CYCLE 3000000
+#define MAX_EXEC_CYCLE 200000
+
+#define MAX_SLEEP_TIME 10 /* in ms */
+
+void virt_machine_run(void *opaque)
+{
+ VirtMachine *m = opaque;
+ int delay, i;
+ FBDevice *fb_dev;
+
+ if (m->console_dev && virtio_console_can_write_data(m->console_dev)) {
+ uint8_t buf[128];
+ int ret, len;
+ len = virtio_console_get_write_len(m->console_dev);
+ len = min_int(len, sizeof(buf));
+ ret = m->console->read_data(m->console->opaque, buf, len);
+ if (ret > 0)
+ virtio_console_write_data(m->console_dev, buf, ret);
+ if (console_resize_pending) {
+ int w, h;
+ console_get_size(&w, &h);
+ virtio_console_resize_event(m->console_dev, w, h);
+ console_resize_pending = FALSE;
+ }
+ }
+
+ fb_dev = m->fb_dev;
+ if (fb_dev) {
+ /* refresh the display */
+ fb_dev->refresh(fb_dev, fb_refresh1, NULL);
+ }
+
+ i = 0;
+ for(;;) {
+ /* wait for an event: the only asynchronous event is the RTC timer */
+ delay = virt_machine_get_sleep_duration(m, MAX_SLEEP_TIME);
+ if (delay != 0 || i >= MAX_EXEC_TOTAL_CYCLE / MAX_EXEC_CYCLE)
+ break;
+ virt_machine_interp(m, MAX_EXEC_CYCLE);
+ i++;
+ }
+
+ if (delay == 0) {
+ emscripten_async_call(virt_machine_run, m, 0);
+ } else {
+ emscripten_async_call(virt_machine_run, m, MAX_SLEEP_TIME);
+ }
+}
+