| /* |
| * 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; |
| 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 = (uint64_t)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 = 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; |
| } |