835 lines
21 KiB
C
835 lines
21 KiB
C
/*
|
|
* 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);
|
|
(void)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);
|
|
(void)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;
|
|
(void)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;
|
|
}
|