diff options
Diffstat (limited to 'jslinux-2019-12-21/tinyemu-2019-12-21/temu.c')
| -rw-r--r-- | jslinux-2019-12-21/tinyemu-2019-12-21/temu.c | 835 |
1 files changed, 835 insertions, 0 deletions
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/temu.c b/jslinux-2019-12-21/tinyemu-2019-12-21/temu.c new file mode 100644 index 0000000..7c07f3b --- /dev/null +++ b/jslinux-2019-12-21/tinyemu-2019-12-21/temu.c @@ -0,0 +1,835 @@ +/* + * TinyEMU + * + * Copyright (c) 2016-2018 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 <getopt.h> +#ifndef _WIN32 +#include <termios.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <linux/if_tun.h> +#endif +#include <sys/stat.h> +#include <signal.h> + +#include "cutils.h" +#include "iomem.h" +#include "virtio.h" +#include "machine.h" +#ifdef CONFIG_FS_NET +#include "fs_utils.h" +#include "fs_wget.h" +#endif +#ifdef CONFIG_SLIRP +#include "slirp/libslirp.h" +#endif + +#ifndef _WIN32 + +typedef struct { + int stdin_fd; + int console_esc_state; + BOOL resize_pending; +} STDIODevice; + +static struct termios oldtty; +static int old_fd0_flags; +static STDIODevice *global_stdio_device; + +static void term_exit(void) +{ + tcsetattr (0, TCSANOW, &oldtty); + fcntl(0, F_SETFL, old_fd0_flags); +} + +static void term_init(BOOL allow_ctrlc) +{ + struct termios tty; + + memset(&tty, 0, sizeof(tty)); + tcgetattr (0, &tty); + oldtty = tty; + old_fd0_flags = fcntl(0, F_GETFL); + + tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP + |INLCR|IGNCR|ICRNL|IXON); + tty.c_oflag |= OPOST; + tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN); + if (!allow_ctrlc) + tty.c_lflag &= ~ISIG; + tty.c_cflag &= ~(CSIZE|PARENB); + tty.c_cflag |= CS8; + tty.c_cc[VMIN] = 1; + tty.c_cc[VTIME] = 0; + + tcsetattr (0, TCSANOW, &tty); + + atexit(term_exit); +} + +static void console_write(void *opaque, const uint8_t *buf, int len) +{ + fwrite(buf, 1, len, stdout); + fflush(stdout); +} + +static int console_read(void *opaque, uint8_t *buf, int len) +{ + STDIODevice *s = opaque; + int ret, i, j; + uint8_t ch; + + if (len <= 0) + return 0; + + ret = read(s->stdin_fd, buf, len); + if (ret < 0) + return 0; + if (ret == 0) { + /* EOF */ + exit(1); + } + + j = 0; + for(i = 0; i < ret; i++) { + ch = buf[i]; + if (s->console_esc_state) { + s->console_esc_state = 0; + switch(ch) { + case 'x': + printf("Terminated\n"); + exit(0); + case 'h': + printf("\n" + "C-a h print this help\n" + "C-a x exit emulator\n" + "C-a C-a send C-a\n" + ); + break; + case 1: + goto output_char; + default: + break; + } + } else { + if (ch == 1) { + s->console_esc_state = 1; + } else { + output_char: + buf[j++] = ch; + } + } + } + return j; +} + +static void term_resize_handler(int sig) +{ + if (global_stdio_device) + global_stdio_device->resize_pending = TRUE; +} + +static void console_get_size(STDIODevice *s, int *pw, int *ph) +{ + struct winsize ws; + int width, height; + /* default values */ + width = 80; + height = 25; + if (ioctl(s->stdin_fd, TIOCGWINSZ, &ws) == 0 && + ws.ws_col >= 4 && ws.ws_row >= 4) { + width = ws.ws_col; + height = ws.ws_row; + } + *pw = width; + *ph = height; +} + +CharacterDevice *console_init(BOOL allow_ctrlc) +{ + CharacterDevice *dev; + STDIODevice *s; + struct sigaction sig; + + term_init(allow_ctrlc); + + dev = mallocz(sizeof(*dev)); + s = mallocz(sizeof(*s)); + s->stdin_fd = 0; + /* Note: the glibc does not properly tests the return value of + write() in printf, so some messages on stdout may be lost */ + fcntl(s->stdin_fd, F_SETFL, O_NONBLOCK); + + s->resize_pending = TRUE; + global_stdio_device = s; + + /* use a signal to get the host terminal resize events */ + sig.sa_handler = term_resize_handler; + sigemptyset(&sig.sa_mask); + sig.sa_flags = 0; + sigaction(SIGWINCH, &sig, NULL); + + dev->opaque = s; + dev->write_data = console_write; + dev->read_data = console_read; + return dev; +} + +#endif /* !_WIN32 */ + +typedef enum { + BF_MODE_RO, + BF_MODE_RW, + BF_MODE_SNAPSHOT, +} BlockDeviceModeEnum; + +#define SECTOR_SIZE 512 + +typedef struct BlockDeviceFile { + FILE *f; + int64_t nb_sectors; + BlockDeviceModeEnum mode; + uint8_t **sector_table; +} BlockDeviceFile; + +static int64_t bf_get_sector_count(BlockDevice *bs) +{ + BlockDeviceFile *bf = bs->opaque; + return bf->nb_sectors; +} + +//#define DUMP_BLOCK_READ + +static int bf_read_async(BlockDevice *bs, + uint64_t sector_num, uint8_t *buf, int n, + BlockDeviceCompletionFunc *cb, void *opaque) +{ + BlockDeviceFile *bf = bs->opaque; + // printf("bf_read_async: sector_num=%" PRId64 " n=%d\n", sector_num, n); +#ifdef DUMP_BLOCK_READ + { + static FILE *f; + if (!f) + f = fopen("/tmp/read_sect.txt", "wb"); + fprintf(f, "%" PRId64 " %d\n", sector_num, n); + } +#endif + if (!bf->f) + return -1; + if (bf->mode == BF_MODE_SNAPSHOT) { + int i; + for(i = 0; i < n; i++) { + if (!bf->sector_table[sector_num]) { + fseek(bf->f, sector_num * SECTOR_SIZE, SEEK_SET); + fread(buf, 1, SECTOR_SIZE, bf->f); + } else { + memcpy(buf, bf->sector_table[sector_num], SECTOR_SIZE); + } + sector_num++; + buf += SECTOR_SIZE; + } + } else { + fseek(bf->f, sector_num * SECTOR_SIZE, SEEK_SET); + fread(buf, 1, n * SECTOR_SIZE, bf->f); + } + /* synchronous read */ + return 0; +} + +static int bf_write_async(BlockDevice *bs, + uint64_t sector_num, const uint8_t *buf, int n, + BlockDeviceCompletionFunc *cb, void *opaque) +{ + BlockDeviceFile *bf = bs->opaque; + int ret; + + switch(bf->mode) { + case BF_MODE_RO: + ret = -1; /* error */ + break; + case BF_MODE_RW: + fseek(bf->f, sector_num * SECTOR_SIZE, SEEK_SET); + fwrite(buf, 1, n * SECTOR_SIZE, bf->f); + ret = 0; + break; + case BF_MODE_SNAPSHOT: + { + int i; + if ((sector_num + n) > bf->nb_sectors) + return -1; + for(i = 0; i < n; i++) { + if (!bf->sector_table[sector_num]) { + bf->sector_table[sector_num] = malloc(SECTOR_SIZE); + } + memcpy(bf->sector_table[sector_num], buf, SECTOR_SIZE); + sector_num++; + buf += SECTOR_SIZE; + } + ret = 0; + } + break; + default: + abort(); + } + + return ret; +} + +static BlockDevice *block_device_init(const char *filename, + BlockDeviceModeEnum mode) +{ + BlockDevice *bs; + BlockDeviceFile *bf; + int64_t file_size; + FILE *f; + const char *mode_str; + + if (mode == BF_MODE_RW) { + mode_str = "r+b"; + } else { + mode_str = "rb"; + } + + f = fopen(filename, mode_str); + if (!f) { + perror(filename); + exit(1); + } + fseek(f, 0, SEEK_END); + file_size = ftello(f); + + bs = mallocz(sizeof(*bs)); + bf = mallocz(sizeof(*bf)); + + bf->mode = mode; + bf->nb_sectors = file_size / 512; + bf->f = f; + + if (mode == BF_MODE_SNAPSHOT) { + bf->sector_table = mallocz(sizeof(bf->sector_table[0]) * + bf->nb_sectors); + } + + bs->opaque = bf; + bs->get_sector_count = bf_get_sector_count; + bs->read_async = bf_read_async; + bs->write_async = bf_write_async; + return bs; +} + +#ifndef _WIN32 + +typedef struct { + int fd; + BOOL select_filled; +} TunState; + +static void tun_write_packet(EthernetDevice *net, + const uint8_t *buf, int len) +{ + TunState *s = net->opaque; + write(s->fd, buf, len); +} + +static void tun_select_fill(EthernetDevice *net, int *pfd_max, + fd_set *rfds, fd_set *wfds, fd_set *efds, + int *pdelay) +{ + TunState *s = net->opaque; + int net_fd = s->fd; + + s->select_filled = net->device_can_write_packet(net); + if (s->select_filled) { + FD_SET(net_fd, rfds); + *pfd_max = max_int(*pfd_max, net_fd); + } +} + +static void tun_select_poll(EthernetDevice *net, + fd_set *rfds, fd_set *wfds, fd_set *efds, + int select_ret) +{ + TunState *s = net->opaque; + int net_fd = s->fd; + uint8_t buf[2048]; + int ret; + + if (select_ret <= 0) + return; + if (s->select_filled && FD_ISSET(net_fd, rfds)) { + ret = read(net_fd, buf, sizeof(buf)); + if (ret > 0) + net->device_write_packet(net, buf, ret); + } + +} + +/* configure with: +# bridge configuration (connect tap0 to bridge interface br0) + ip link add br0 type bridge + ip tuntap add dev tap0 mode tap [user x] [group x] + ip link set tap0 master br0 + ip link set dev br0 up + ip link set dev tap0 up + +# NAT configuration (eth1 is the interface connected to internet) + ifconfig br0 192.168.3.1 + echo 1 > /proc/sys/net/ipv4/ip_forward + iptables -D FORWARD 1 + iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE + + In the VM: + ifconfig eth0 192.168.3.2 + route add -net 0.0.0.0 netmask 0.0.0.0 gw 192.168.3.1 +*/ +static EthernetDevice *tun_open(const char *ifname) +{ + struct ifreq ifr; + int fd, ret; + EthernetDevice *net; + TunState *s; + + fd = open("/dev/net/tun", O_RDWR); + if (fd < 0) { + fprintf(stderr, "Error: could not open /dev/net/tun\n"); + return NULL; + } + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + pstrcpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname); + ret = ioctl(fd, TUNSETIFF, (void *) &ifr); + if (ret != 0) { + fprintf(stderr, "Error: could not configure /dev/net/tun\n"); + close(fd); + return NULL; + } + fcntl(fd, F_SETFL, O_NONBLOCK); + + net = mallocz(sizeof(*net)); + net->mac_addr[0] = 0x02; + net->mac_addr[1] = 0x00; + net->mac_addr[2] = 0x00; + net->mac_addr[3] = 0x00; + net->mac_addr[4] = 0x00; + net->mac_addr[5] = 0x01; + s = mallocz(sizeof(*s)); + s->fd = fd; + net->opaque = s; + net->write_packet = tun_write_packet; + net->select_fill = tun_select_fill; + net->select_poll = tun_select_poll; + return net; +} + +#endif /* !_WIN32 */ + +#ifdef CONFIG_SLIRP + +/*******************************************************/ +/* slirp */ + +static Slirp *slirp_state; + +static void slirp_write_packet(EthernetDevice *net, + const uint8_t *buf, int len) +{ + Slirp *slirp_state = net->opaque; + slirp_input(slirp_state, buf, len); +} + +int slirp_can_output(void *opaque) +{ + EthernetDevice *net = opaque; + return net->device_can_write_packet(net); +} + +void slirp_output(void *opaque, const uint8_t *pkt, int pkt_len) +{ + EthernetDevice *net = opaque; + return net->device_write_packet(net, pkt, pkt_len); +} + +static void slirp_select_fill1(EthernetDevice *net, int *pfd_max, + fd_set *rfds, fd_set *wfds, fd_set *efds, + int *pdelay) +{ + Slirp *slirp_state = net->opaque; + slirp_select_fill(slirp_state, pfd_max, rfds, wfds, efds); +} + +static void slirp_select_poll1(EthernetDevice *net, + fd_set *rfds, fd_set *wfds, fd_set *efds, + int select_ret) +{ + Slirp *slirp_state = net->opaque; + slirp_select_poll(slirp_state, rfds, wfds, efds, (select_ret <= 0)); +} + +static EthernetDevice *slirp_open(void) +{ + EthernetDevice *net; + struct in_addr net_addr = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */ + struct in_addr mask = { .s_addr = htonl(0xffffff00) }; /* 255.255.255.0 */ + struct in_addr host = { .s_addr = htonl(0x0a000202) }; /* 10.0.2.2 */ + struct in_addr dhcp = { .s_addr = htonl(0x0a00020f) }; /* 10.0.2.15 */ + struct in_addr dns = { .s_addr = htonl(0x0a000203) }; /* 10.0.2.3 */ + const char *bootfile = NULL; + const char *vhostname = NULL; + int restricted = 0; + + if (slirp_state) { + fprintf(stderr, "Only a single slirp instance is allowed\n"); + return NULL; + } + net = mallocz(sizeof(*net)); + + slirp_state = slirp_init(restricted, net_addr, mask, host, vhostname, + "", bootfile, dhcp, dns, net); + + net->mac_addr[0] = 0x02; + net->mac_addr[1] = 0x00; + net->mac_addr[2] = 0x00; + net->mac_addr[3] = 0x00; + net->mac_addr[4] = 0x00; + net->mac_addr[5] = 0x01; + net->opaque = slirp_state; + net->write_packet = slirp_write_packet; + net->select_fill = slirp_select_fill1; + net->select_poll = slirp_select_poll1; + + return net; +} + +#endif /* CONFIG_SLIRP */ + +#define MAX_EXEC_CYCLE 500000 +#define MAX_SLEEP_TIME 10 /* in ms */ + +void virt_machine_run(VirtMachine *m) +{ + fd_set rfds, wfds, efds; + int fd_max, ret, delay; + struct timeval tv; +#ifndef _WIN32 + int stdin_fd; +#endif + + delay = virt_machine_get_sleep_duration(m, MAX_SLEEP_TIME); + + /* wait for an event */ + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&efds); + fd_max = -1; +#ifndef _WIN32 + if (m->console_dev && virtio_console_can_write_data(m->console_dev)) { + STDIODevice *s = m->console->opaque; + stdin_fd = s->stdin_fd; + FD_SET(stdin_fd, &rfds); + fd_max = stdin_fd; + + if (s->resize_pending) { + int width, height; + console_get_size(s, &width, &height); + virtio_console_resize_event(m->console_dev, width, height); + s->resize_pending = FALSE; + } + } +#endif + if (m->net) { + m->net->select_fill(m->net, &fd_max, &rfds, &wfds, &efds, &delay); + } +#ifdef CONFIG_FS_NET + fs_net_set_fdset(&fd_max, &rfds, &wfds, &efds, &delay); +#endif + tv.tv_sec = delay / 1000; + tv.tv_usec = (delay % 1000) * 1000; + ret = select(fd_max + 1, &rfds, &wfds, &efds, &tv); + if (m->net) { + m->net->select_poll(m->net, &rfds, &wfds, &efds, ret); + } + if (ret > 0) { +#ifndef _WIN32 + if (m->console_dev && FD_ISSET(stdin_fd, &rfds)) { + 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); + } + } +#endif + } + +#ifdef CONFIG_SDL + sdl_refresh(m); +#endif + + virt_machine_interp(m, MAX_EXEC_CYCLE); +} + +/*******************************************************/ + +static struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "ctrlc", no_argument }, + { "rw", no_argument }, + { "ro", no_argument }, + { "append", required_argument }, + { "no-accel", no_argument }, + { "build-preload", required_argument }, + { NULL }, +}; + +void help(void) +{ + printf("temu version " CONFIG_VERSION ", Copyright (c) 2016-2018 Fabrice Bellard\n" + "usage: riscvemu [options] config_file\n" + "options are:\n" + "-m ram_size set the RAM size in MB\n" + "-rw allow write access to the disk image (default=snapshot)\n" + "-ctrlc the C-c key stops the emulator instead of being sent to the\n" + " emulated software\n" + "-append cmdline append cmdline to the kernel command line\n" + "-no-accel disable VM acceleration (KVM, x86 machine only)\n" + "\n" + "Console keys:\n" + "Press C-a x to exit the emulator, C-a h to get some help.\n"); + exit(1); +} + +#ifdef CONFIG_FS_NET +static BOOL net_completed; + +static void net_start_cb(void *arg) +{ + net_completed = TRUE; +} + +static BOOL net_poll_cb(void *arg) +{ + return net_completed; +} + +#endif + +int main(int argc, char **argv) +{ + VirtMachine *s; + const char *path, *cmdline, *build_preload_file; + int c, option_index, i, ram_size, accel_enable; + BOOL allow_ctrlc; + BlockDeviceModeEnum drive_mode; + VirtMachineParams p_s, *p = &p_s; + + ram_size = -1; + allow_ctrlc = FALSE; + (void)allow_ctrlc; + drive_mode = BF_MODE_SNAPSHOT; + accel_enable = -1; + cmdline = NULL; + build_preload_file = NULL; + for(;;) { + c = getopt_long_only(argc, argv, "hm:", options, &option_index); + if (c == -1) + break; + switch(c) { + case 0: + switch(option_index) { + case 1: /* ctrlc */ + allow_ctrlc = TRUE; + break; + case 2: /* rw */ + drive_mode = BF_MODE_RW; + break; + case 3: /* ro */ + drive_mode = BF_MODE_RO; + break; + case 4: /* append */ + cmdline = optarg; + break; + case 5: /* no-accel */ + accel_enable = FALSE; + break; + case 6: /* build-preload */ + build_preload_file = optarg; + break; + default: + fprintf(stderr, "unknown option index: %d\n", option_index); + exit(1); + } + break; + case 'h': + help(); + break; + case 'm': + ram_size = strtoul(optarg, NULL, 0); + break; + default: + exit(1); + } + } + + if (optind >= argc) { + help(); + } + + path = argv[optind++]; + + virt_machine_set_defaults(p); +#ifdef CONFIG_FS_NET + fs_wget_init(); +#endif + virt_machine_load_config_file(p, path, NULL, NULL); +#ifdef CONFIG_FS_NET + fs_net_event_loop(NULL, NULL); +#endif + + /* override some config parameters */ + + if (ram_size > 0) { + p->ram_size = (uint64_t)ram_size << 20; + } + if (accel_enable != -1) + p->accel_enable = accel_enable; + if (cmdline) { + vm_add_cmdline(p, cmdline); + } + + /* open the files & devices */ + for(i = 0; i < p->drive_count; i++) { + BlockDevice *drive; + char *fname; + fname = get_file_path(p->cfg_filename, p->tab_drive[i].filename); +#ifdef CONFIG_FS_NET + if (is_url(fname)) { + net_completed = FALSE; + drive = block_device_init_http(fname, 128 * 1024, + net_start_cb, NULL); + /* wait until the drive is initialized */ + fs_net_event_loop(net_poll_cb, NULL); + } else +#endif + { + drive = block_device_init(fname, drive_mode); + } + free(fname); + p->tab_drive[i].block_dev = drive; + } + + for(i = 0; i < p->fs_count; i++) { + FSDevice *fs; + const char *path; + path = p->tab_fs[i].filename; +#ifdef CONFIG_FS_NET + if (is_url(path)) { + fs = fs_net_init(path, NULL, NULL); + if (!fs) + exit(1); + if (build_preload_file) + fs_dump_cache_load(fs, build_preload_file); + fs_net_event_loop(NULL, NULL); + } else +#endif + { +#ifdef _WIN32 + fprintf(stderr, "Filesystem access not supported yet\n"); + exit(1); +#else + char *fname; + fname = get_file_path(p->cfg_filename, path); + fs = fs_disk_init(fname); + if (!fs) { + fprintf(stderr, "%s: must be a directory\n", fname); + exit(1); + } + free(fname); +#endif + } + p->tab_fs[i].fs_dev = fs; + } + + for(i = 0; i < p->eth_count; i++) { +#ifdef CONFIG_SLIRP + if (!strcmp(p->tab_eth[i].driver, "user")) { + p->tab_eth[i].net = slirp_open(); + if (!p->tab_eth[i].net) + exit(1); + } else +#endif +#ifndef _WIN32 + if (!strcmp(p->tab_eth[i].driver, "tap")) { + p->tab_eth[i].net = tun_open(p->tab_eth[i].ifname); + if (!p->tab_eth[i].net) + exit(1); + } else +#endif + { + fprintf(stderr, "Unsupported network driver '%s'\n", + p->tab_eth[i].driver); + exit(1); + } + } + +#ifdef CONFIG_SDL + if (p->display_device) { + sdl_init(p->width, p->height); + } else +#endif + { +#ifdef _WIN32 + fprintf(stderr, "Console not supported yet\n"); + exit(1); +#else + p->console = console_init(allow_ctrlc); +#endif + } + p->rtc_real_time = TRUE; + + s = virt_machine_init(p); + if (!s) + exit(1); + + virt_machine_free_config(p); + + if (s->net) { + s->net->device_set_carrier(s->net, TRUE); + } + + for(;;) { + virt_machine_run(s); + } + virt_machine_end(s); + return 0; +} |
