*.vr
*.d
*.o
+*.orig
*~
- dyngen-xen
+
+ .pc
+ patches
+ pc-bios/bios-pq/status
+ pc-bios/vgabios-pq/status
++
+i386-dm/Makefile
+i386-dm/config.mak
+i386-dm/qemu-dm
++i386-dm/gdbstub-xml.c
+qemu-img-xen
+qemu-nbd-xen
-
- i386-dm/gdbstub-xml.c
+dist
qemu-nbd$(EXESUF): qemu-nbd.o qemu-tool.o osdep.o $(BLOCK_OBJS)
$(CC) $(LDFLAGS) -o $@ $^ -lz $(LIBS)
- # dyngen host tool
- dyngen$(EXESUF): dyngen.o osdep.o
- $(HOST_CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^
+
clean:
# avoid old build problems by removing potentially incorrect old files
rm -f config.mak config.h op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
endif
endif
+default: all
+
+ kvm.o: CFLAGS+=$(KVM_CFLAGS)
+ kvm-all.o: CFLAGS+=$(KVM_CFLAGS)
+
+ all: $(PROGS)
+
#########################################################
# cpu emulator library
LIBOBJS=exec.o kqemu.o translate-all.o cpu-exec.o\
# libqemu
- ifdef CONFIG_DYNGEN_OP
- OPC_H = gen-op.h dyngen-opc.h op.h
- endif
-
+-include hookslib.mak
+-include hookstarget.mak
+
libqemu.a: $(LIBOBJS)
rm -f $@
$(AR) rcs $@ $(LIBOBJS)
ifeq ($(TARGET_BASE_ARCH), i386)
# Hardware support
-OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o
-OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o
+ifdef CONFIG_AUDIO
+OBJS+= $(SOUND_HW) pcspk.o
+CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
+endif
+OBJS+= ide.o pckbd.o ps2.o vga.o dma.o
+OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o
OBJS+= cirrus_vga.o apic.o parallel.o acpi.o piix_pci.o
- OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o
+ OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o hpet.o
-CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
endif
ifeq ($(TARGET_BASE_ARCH), ppc)
CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
$(CC) $(CPPFLAGS) -c -o $@ $<
clean:
- rm -f *.o *.a *~ $(PROGS) gen-op.h dyngen-opc.h op.h nwfpe/*.o fpu/*.o
+ rm -f *.o *.a *~ $(PROGS) nwfpe/*.o fpu/*.o
rm -f *.d */*.d tcg/*.o
-install: all
+install: all install-hook
ifneq ($(PROGS),)
$(INSTALL) -m 755 -s $(PROGS) "$(DESTDIR)$(bindir)"
endif
static int posix_aio_init(void)
{
struct sigaction act;
+ sigset_t enable;
PosixAioState *s;
int fds[2];
+ struct qemu_paioinit ai;
if (posix_aio_state)
return 0;
TextCell *cells;
int text_x[2], text_y[2], cursor_invalidate;
++ int update_x0;
++ int update_y0;
++ int update_x1;
++ int update_y1;
++
enum TTYState state;
int esc_params[MAX_ESC_PARAMS];
int nb_esc_params;
{
unsigned int r, g, b, color;
- switch(ds->depth) {
+ switch(ds_get_bits_per_pixel(ds)) {
-#if 0
case 8:
r = (rgba >> 16) & 0xff;
g = (rgba >> 8) & 0xff;
s->cells = cells;
}
-static inline void text_update_xy(TextConsole *s, int x, int y)
++static void invalidate_xy(TextConsole *s, int x, int y)
+ {
- s->text_x[0] = MIN(s->text_x[0], x);
- s->text_x[1] = MAX(s->text_x[1], x);
- s->text_y[0] = MIN(s->text_y[0], y);
- s->text_y[1] = MAX(s->text_y[1], y);
++ if (s->update_x0 > x * FONT_WIDTH)
++ s->update_x0 = x * FONT_WIDTH;
++ if (s->update_y0 > y * FONT_HEIGHT)
++ s->update_y0 = y * FONT_HEIGHT;
++ if (s->update_x1 < (x + 1) * FONT_WIDTH)
++ s->update_x1 = (x + 1) * FONT_WIDTH;
++ if (s->update_y1 < (y + 1) * FONT_HEIGHT)
++ s->update_y1 = (y + 1) * FONT_HEIGHT;
+ }
+
static void update_xy(TextConsole *s, int x, int y)
{
TextCell *c;
c = &s->cells[y1 * s->width + x];
vga_putcharxy(s->ds, x, y2, c->ch,
&(c->t_attrib));
-- dpy_update(s->ds, x * FONT_WIDTH, y2 * FONT_HEIGHT,
-- FONT_WIDTH, FONT_HEIGHT);
++ invalidate_xy(s, x, y2);
}
}
}
if (show) {
TextAttributes t_attrib = s->t_attrib_default;
t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
- vga_putcharxy(s->ds, s->x, y, c->ch, &t_attrib);
+ vga_putcharxy(s->ds, x, y, c->ch, &t_attrib);
} else {
- vga_putcharxy(s->ds, s->x, y, c->ch,
- &(c->t_attrib));
+ vga_putcharxy(s->ds, x, y, c->ch, &(c->t_attrib));
}
- dpy_update(s->ds, s->x * FONT_WIDTH, y * FONT_HEIGHT,
- dpy_update(s->ds, x * FONT_WIDTH, y * FONT_HEIGHT,
-- FONT_WIDTH, FONT_HEIGHT);
++ invalidate_xy(s, x, y);
}
}
}
if (s != active_console)
return;
- if (!ds_get_bits_per_pixel(s->ds)) {
- s->text_x[0] = 0;
- s->text_y[0] = 0;
- s->text_x[1] = s->width - 1;
- s->text_y[1] = s->height - 1;
- s->cursor_invalidate = 1;
- return;
- }
- vga_fill_rect(s->ds, 0, 0, s->ds->width, s->ds->height,
+ vga_fill_rect(s->ds, 0, 0, ds_get_width(s->ds), ds_get_height(s->ds),
color_table[0][COLOR_BLACK]);
y1 = s->y_displayed;
for(y = 0; y < s->height; y++) {
if (++y1 == s->total_height)
y1 = 0;
}
- dpy_update(s->ds, 0, 0, s->ds->width, s->ds->height);
- dpy_update(s->ds, 0, 0, ds_get_width(s->ds), ds_get_height(s->ds));
console_show_cursor(s, 1);
++ dpy_update(s->ds, 0, 0, ds_get_width(s->ds), ds_get_height(s->ds));
}
static void console_scroll(int ydelta)
vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT,
s->width * FONT_WIDTH, FONT_HEIGHT,
color_table[0][s->t_attrib_default.bgcol]);
-- dpy_update(s->ds, 0, 0,
-- s->width * FONT_WIDTH, s->height * FONT_HEIGHT);
++ s->update_x0 = 0;
++ s->update_y0 = 0;
++ s->update_x1 = s->width * FONT_WIDTH;
++ s->update_y1 = s->height * FONT_HEIGHT;
}
}
}
if (index >= MAX_CONSOLES)
return;
++ active_console->g_width = ds_get_width(active_console->ds);
++ active_console->g_height = ds_get_height(active_console->ds);
s = consoles[index];
if (s) {
++ DisplayState *ds = s->ds;
active_console = s;
- if (s->console_type == TEXT_CONSOLE) {
- if (s->g_width != s->ds->width ||
- s->g_height != s->ds->height) {
- s->g_width = s->ds->width;
- s->g_height = s->ds->height;
- text_console_resize(s);
- }
- console_refresh(s);
- if (s->console_type != TEXT_CONSOLE && s->g_width && s->g_height
- && (s->g_width != ds_get_width(s->ds) || s->g_height != ds_get_height(s->ds)))
- dpy_resize(s->ds, s->g_width, s->g_height);
++ if (ds_get_bits_per_pixel(s->ds)) {
++ ds->surface = qemu_resize_displaysurface(ds, s->g_width,
++ s->g_height, 32, 4 * s->g_width);
+ } else {
- vga_hw_invalidate();
++ s->ds->surface->width = s->width;
++ s->ds->surface->height = s->height;
+ }
++ dpy_resize(s->ds);
+ vga_hw_invalidate();
}
}
TextConsole *s = chr->opaque;
int i;
++ s->update_x0 = s->width * FONT_WIDTH;
++ s->update_y0 = s->height * FONT_HEIGHT;
++ s->update_x1 = 0;
++ s->update_y1 = 0;
console_show_cursor(s, 0);
for(i = 0; i < len; i++) {
console_putchar(s, buf[i]);
}
console_show_cursor(s, 1);
++ if (ds_get_bits_per_pixel(s->ds) && s->update_x0 < s->update_x1) {
++ dpy_update(s->ds, s->update_x0, s->update_y0,
++ s->update_x1 - s->update_x0,
++ s->update_y1 - s->update_y0);
++ }
return len;
}
static void text_console_invalidate(void *opaque)
{
TextConsole *s = (TextConsole *) opaque;
--
- if (s->g_width != s->ds->width || s->g_height != s->ds->height) {
- if (s->g_width != ds_get_width(s->ds) || s->g_height != ds_get_height(s->ds)) {
-- if (s->console_type == TEXT_CONSOLE_FIXED_SIZE)
-- dpy_resize(s->ds, s->g_width, s->g_height);
-- else {
- s->g_width = s->ds->width;
- s->g_height = s->ds->height;
- s->g_width = ds_get_width(s->ds);
- s->g_height = ds_get_height(s->ds);
-- text_console_resize(s);
-- }
++ if (!ds_get_bits_per_pixel(s->ds) && s->console_type == TEXT_CONSOLE) {
++ s->g_width = ds_get_width(s->ds);
++ s->g_height = ds_get_height(s->ds);
++ text_console_resize(s);
}
console_refresh(s);
}
}
}
++static TextConsole *get_graphic_console(DisplayState *ds)
++{
++ int i;
++ TextConsole *s;
++ for (i = 0; i < nb_consoles; i++) {
++ s = consoles[i];
++ if (s->console_type == GRAPHIC_CONSOLE && s->ds == ds)
++ return s;
++ }
++ return NULL;
++}
++
static TextConsole *new_console(DisplayState *ds, console_type_t console_type)
{
TextConsole *s;
consoles[i] = consoles[i - 1];
}
consoles[i] = s;
++ nb_consoles++;
}
return s;
}
--TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update,
-- vga_hw_invalidate_ptr invalidate,
-- vga_hw_screen_dump_ptr screen_dump,
-- vga_hw_text_update_ptr text_update,
-- void *opaque)
++DisplayState *graphic_console_init(vga_hw_update_ptr update,
++ vga_hw_invalidate_ptr invalidate,
++ vga_hw_screen_dump_ptr screen_dump,
++ vga_hw_text_update_ptr text_update,
++ void *opaque)
{
TextConsole *s;
++ DisplayState *ds;
++
++ ds = (DisplayState *) qemu_mallocz(sizeof(DisplayState));
++ if (ds == NULL)
++ return NULL;
++ ds->allocator = &default_allocator;
++ ds->surface = qemu_create_displaysurface(ds, 640, 480, 32, 640 * 4);
s = new_console(ds, GRAPHIC_CONSOLE);
-- if (!s)
-- return NULL;
++ if (s == NULL) {
++ qemu_free_displaysurface(ds);
++ qemu_free(ds);
++ return NULL;
++ }
s->hw_update = update;
s->hw_invalidate = invalidate;
s->hw_screen_dump = screen_dump;
s->hw_text_update = text_update;
s->hw = opaque;
-- return s;
++
++ register_displaystate(ds);
++ return ds;
}
int is_graphic_console(void)
}
}
--CharDriverState *text_console_init(DisplayState *ds, const char *p)
++static int n_text_consoles;
++static CharDriverState *text_consoles[128];
++static char *text_console_strs[128];
++
++static void text_console_do_init(CharDriverState *chr, DisplayState *ds, const char *p)
{
-- CharDriverState *chr;
TextConsole *s;
+ unsigned width;
+ unsigned height;
static int color_inited;
-- chr = qemu_mallocz(sizeof(CharDriverState));
-- if (!chr)
-- return NULL;
s = new_console(ds, (p == 0) ? TEXT_CONSOLE : TEXT_CONSOLE_FIXED_SIZE);
if (!s) {
free(chr);
-- return NULL;
++ return;
}
chr->opaque = s;
chr->chr_write = console_puts;
s->out_fifo.buf = s->out_fifo_buf;
s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
s->kbd_timer = qemu_new_timer(rt_clock, kbd_send_chars, s);
++ s->ds = ds;
if (!color_inited) {
color_inited = 1;
text_console_resize(s);
qemu_chr_reset(chr);
++ if (chr->init)
++ chr->init(chr);
++}
++
++CharDriverState *text_console_init(const char *p)
++{
++ CharDriverState *chr;
++
++ chr = qemu_mallocz(sizeof(CharDriverState));
++ if (!chr)
++ return NULL;
++
++ if (n_text_consoles == 128) {
++ fprintf(stderr, "Too many text consoles\n");
++ exit(1);
++ }
++ text_consoles[n_text_consoles] = chr;
++ text_console_strs[n_text_consoles] = p ? qemu_strdup(p) : NULL;
++ n_text_consoles++;
return chr;
}
--void qemu_console_resize(QEMUConsole *console, int width, int height)
++void text_consoles_set_display(DisplayState *ds)
{
-- if (console->g_width != width || console->g_height != height
- || !console->ds->data) {
- || !ds_get_data(console->ds)) {
-- console->g_width = width;
-- console->g_height = height;
-- if (active_console == console) {
-- dpy_resize(console->ds, width, height);
-- }
++ int i;
++
++ for (i = 0; i < n_text_consoles; i++) {
++ text_console_do_init(text_consoles[i], ds, text_console_strs[i]);
++ qemu_free(text_console_strs[i]);
}
++
++ n_text_consoles = 0;
}
--void qemu_console_copy(QEMUConsole *console, int src_x, int src_y,
++void qemu_console_resize(DisplayState *ds, int width, int height)
++{
++ TextConsole *s = get_graphic_console(ds);
++ if (!s) return;
++ s->g_width = width;
++ s->g_height = height;
++ if (is_graphic_console()) {
++ ds->surface = qemu_resize_displaysurface(ds, width, height, 32, 4 * width);
++ dpy_resize(ds);
++ }
++}
++
++void qemu_console_copy(DisplayState *ds, int src_x, int src_y,
int dst_x, int dst_y, int w, int h)
{
-- if (active_console == console) {
-- if (console->ds->dpy_copy)
-- console->ds->dpy_copy(console->ds,
-- src_x, src_y, dst_x, dst_y, w, h);
-- else {
-- /* TODO */
-- console->ds->dpy_update(console->ds, dst_x, dst_y, w, h);
-- }
++ if (is_graphic_console()) {
++ dpy_copy(ds, src_x, src_y, dst_x, dst_y, w, h);
++ }
++}
++
++PixelFormat qemu_different_endianness_pixelformat(int bpp)
++{
++ PixelFormat pf;
++
++ memset(&pf, 0x00, sizeof(PixelFormat));
++
++ pf.bits_per_pixel = bpp;
++ pf.bytes_per_pixel = bpp / 8;
++ pf.depth = bpp == 32 ? 24 : bpp;
++
++ switch (bpp) {
++ case 24:
++ pf.rmask = 0x000000FF;
++ pf.gmask = 0x0000FF00;
++ pf.bmask = 0x00FF0000;
++ pf.rmax = 255;
++ pf.gmax = 255;
++ pf.bmax = 255;
++ pf.rshift = 0;
++ pf.gshift = 8;
++ pf.bshift = 16;
++ pf.rbits = 8;
++ pf.gbits = 8;
++ pf.bbits = 8;
++ break;
++ case 32:
++ pf.rmask = 0x0000FF00;
++ pf.gmask = 0x00FF0000;
++ pf.bmask = 0xFF000000;
++ pf.amask = 0x00000000;
++ pf.amax = 255;
++ pf.rmax = 255;
++ pf.gmax = 255;
++ pf.bmax = 255;
++ pf.ashift = 0;
++ pf.rshift = 8;
++ pf.gshift = 16;
++ pf.bshift = 24;
++ pf.rbits = 8;
++ pf.gbits = 8;
++ pf.bbits = 8;
++ pf.abits = 8;
++ break;
++ default:
++ break;
+ }
++ return pf;
++}
++
++PixelFormat qemu_default_pixelformat(int bpp)
++{
++ PixelFormat pf;
++
++ memset(&pf, 0x00, sizeof(PixelFormat));
++
++ pf.bits_per_pixel = bpp;
++ pf.bytes_per_pixel = bpp / 8;
++ pf.depth = bpp == 32 ? 24 : bpp;
++
++ switch (bpp) {
++ case 16:
++ pf.rmask = 0x0000F800;
++ pf.gmask = 0x000007E0;
++ pf.bmask = 0x0000001F;
++ pf.rmax = 31;
++ pf.gmax = 63;
++ pf.bmax = 31;
++ pf.rshift = 11;
++ pf.gshift = 5;
++ pf.bshift = 0;
++ pf.rbits = 5;
++ pf.gbits = 6;
++ pf.bbits = 5;
++ break;
++ case 24:
++ pf.rmask = 0x00FF0000;
++ pf.gmask = 0x0000FF00;
++ pf.bmask = 0x000000FF;
++ pf.rmax = 255;
++ pf.gmax = 255;
++ pf.bmax = 255;
++ pf.rshift = 16;
++ pf.gshift = 8;
++ pf.bshift = 0;
++ pf.rbits = 8;
++ pf.gbits = 8;
++ pf.bbits = 8;
++ case 32:
++ pf.rmask = 0x00FF0000;
++ pf.gmask = 0x0000FF00;
++ pf.bmask = 0x000000FF;
++ pf.amax = 255;
++ pf.rmax = 255;
++ pf.gmax = 255;
++ pf.bmax = 255;
++ pf.ashift = 24;
++ pf.rshift = 16;
++ pf.gshift = 8;
++ pf.bshift = 0;
++ pf.rbits = 8;
++ pf.gbits = 8;
++ pf.bbits = 8;
++ pf.abits = 8;
++ break;
++ default:
++ break;
++ }
++ return pf;
++}
++
++DisplaySurface* defaultallocator_create_displaysurface(int width, int height, int bpp, int linesize)
++{
++ DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface));
++ if (surface == NULL) {
++ fprintf(stderr, "defaultallocator_create_displaysurface: malloc failed\n");
++ exit(1);
+ }
++
++ surface->width = width;
++ surface->height = height;
++ surface->linesize = linesize;
++ surface->pf = qemu_default_pixelformat(bpp);
++#ifdef WORDS_BIGENDIAN
++ surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG;
++#else
++ surface->flags = QEMU_ALLOCATED_FLAG;
++#endif
++ surface->data = (uint8_t*) qemu_mallocz(surface->linesize * surface->height);
++ if (surface->data == NULL) {
++ fprintf(stderr, "defaultallocator_create_displaysurface: malloc failed\n");
++ exit(1);
++ }
++
++ return surface;
++}
++
++DisplaySurface* defaultallocator_resize_displaysurface(DisplaySurface *surface,
++ int width, int height, int bpp, int linesize)
++{
++ surface->width = width;
++ surface->height = height;
++ surface->linesize = linesize;
++ surface->pf = qemu_default_pixelformat(bpp);
++ if (surface->flags & QEMU_ALLOCATED_FLAG)
++ surface->data = (uint8_t*) qemu_realloc(surface->data, surface->linesize * surface->height);
++ else
++ surface->data = (uint8_t*) qemu_malloc(surface->linesize * surface->height);
++ if (surface->data == NULL) {
++ fprintf(stderr, "defaultallocator_resize_displaysurface: malloc failed\n");
++ exit(1);
++ }
++#ifdef WORDS_BIGENDIAN
++ surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG;
++#else
++ surface->flags = QEMU_ALLOCATED_FLAG;
++#endif
++
++ return surface;
++}
++
++DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp,
++ int linesize, uint8_t *data)
++{
++ DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface));
++ if (surface == NULL) {
++ fprintf(stderr, "qemu_create_displaysurface_from: malloc failed\n");
++ exit(1);
++ }
++
++ surface->width = width;
++ surface->height = height;
++ surface->linesize = linesize;
++ surface->pf = qemu_default_pixelformat(bpp);
++#ifdef WORDS_BIGENDIAN
++ surface->flags = QEMU_BIG_ENDIAN_FLAG;
++#endif
++ surface->data = data;
++
++ return surface;
++}
++
++void defaultallocator_free_displaysurface(DisplaySurface *surface)
++{
++ if (surface == NULL)
++ return;
++ if (surface->flags & QEMU_ALLOCATED_FLAG)
++ qemu_free(surface->data);
++ qemu_free(surface);
}
* THE SOFTWARE.
*/
#include "qemu-common.h"
+ #include "host-utils.h"
-void pstrcpy(char *buf, int buf_size, const char *str)
+void pstrcpy(char *buf, size_t buf_size, const char *str)
{
int c;
char *q = buf;
s->sr[reg_index] = (s->sr[reg_index] & 0x38) | (reg_value & 0xc7);
cirrus_update_memory_access(s);
break;
++
++
++ // Extension to allow BIOS to clear 16K VRAM bank in one operation
++ case 0xFE:
++ s->gr[reg_index] = reg_value; // Lower byte of value to be written
++ break;
++ case 0xFF:
++ {
++ target_phys_addr_t addr;
++ for (addr = 0xa0000; addr < 0xa4000; addr += 2)
++ cirrus_vga_mem_writew(s, addr, (reg_value << 8) | s->gr[0xFE]);
++ }
++ break;
++ default:
++
default:
#ifdef DEBUG_CIRRUS
printf("cirrus: outport sr_index %02x, sr_value %02x\n", reg_index,
cirrus_linear_bitblt_writel,
};
-
+ static void map_linear_vram(CirrusVGAState *s)
+ {
++ if (s->lfb_addr && s->lfb_end && s->vram_gmfn != s->lfb_addr) {
++ set_vram_mapping(s, s->lfb_addr, s->lfb_end);
++ }
+ if (!s->map_addr && s->lfb_addr && s->lfb_end) {
+ s->map_addr = s->lfb_addr;
+ s->map_end = s->lfb_end;
+ cpu_register_physical_memory(s->map_addr, s->map_end - s->map_addr, s->vram_offset);
+ vga_dirty_log_start((VGAState *)s);
+ }
+
+ if (!s->map_addr)
+ return;
+
+ s->lfb_vram_mapped = 0;
+
+ if (!(s->cirrus_srcptr != s->cirrus_srcptr_end)
+ && !((s->sr[0x07] & 0x01) == 0)
+ && !((s->gr[0x0B] & 0x14) == 0x14)
+ && !(s->gr[0x0B] & 0x02)) {
+
+ cpu_register_physical_memory(isa_mem_base + 0xa0000, 0x8000,
+ (s->vram_offset + s->cirrus_bank_base[0]) | IO_MEM_RAM);
+ cpu_register_physical_memory(isa_mem_base + 0xa8000, 0x8000,
+ (s->vram_offset + s->cirrus_bank_base[1]) | IO_MEM_RAM);
+
+ s->lfb_vram_mapped = 1;
+ vga_dirty_log_start((VGAState *)s);
+ }
+ else {
+ cpu_register_physical_memory(isa_mem_base + 0xa0000, 0x8000, s->vga_io_memory);
+ cpu_register_physical_memory(isa_mem_base + 0xa8000, 0x8000, s->vga_io_memory);
+ }
+
+ }
- void cirrus_restart_acc(CirrusVGAState *s)
+ static void unmap_linear_vram(CirrusVGAState *s)
{
- set_vram_mapping(s, s->lfb_addr, s->lfb_end);
++ if (s->lfb_addr && s->lfb_end && s->vram_gmfn != s->lfb_addr) {
++ unset_vram_mapping(s);
++ }
+ if (s->map_addr && s->lfb_addr && s->lfb_end) {
+ vga_dirty_log_stop((VGAState *)s);
+ s->map_addr = s->map_end = 0;
+ }
+
+ cpu_register_physical_memory(isa_mem_base + 0xa0000, 0x20000,
+ s->vga_io_memory);
}
/* Compute the memory access functions */
static int cirrus_vga_load(QEMUFile *f, void *opaque, int version_id)
{
CirrusVGAState *s = opaque;
- uint64_t t;
+ uint8_t vga_acc = 0;
int ret;
- if (version_id > 2)
+ if (version_id > 3)
return -EINVAL;
if (s->pci_dev && version_id >= 2) {
qemu_get_be32s(f, &s->hw_cursor_x);
qemu_get_be32s(f, &s->hw_cursor_y);
+ qemu_get_8s(f, &vga_acc);
+ /* XXX throwing away 32 bits */
+ qemu_get_be32(f);
+ qemu_get_be32s(f, &s->lfb_addr);
+ /* XXX throwing away 32 bits */
+ qemu_get_be32(f);
+ qemu_get_be32s(f, &s->lfb_end);
+
+ if (version_id >= 3)
+ qemu_get_be64s(f, &s->vram_gmfn);
+ t = s->vram_gmfn;
+ if (!s->vram_gmfn) {
+ /* Old guest, VRAM is not mapped, we have to restore it
+ * ourselves */
+ s->vram_gmfn = vga_acc ? s->lfb_addr : VRAM_RESERVED_ADDRESS;
+ xen_vga_populate_vram(s->vram_gmfn, s->vram_size);
+ } else {
+ xen_vga_vram_map(s->vram_gmfn, s->vram_size);
+ }
+ if (version_id < 3 || (!vga_acc && !t))
+ qemu_get_buffer(f, s->vram_ptr, s->vram_size);
+
+ cirrus_update_memory_access(s);
/* force refresh */
s->graphic_mode = -1;
cirrus_update_bank_ptr(s, 0);
s->get_resolution = cirrus_get_resolution;
s->cursor_invalidate = cirrus_cursor_invalidate;
s->cursor_draw_line = cirrus_cursor_draw_line;
+ }
+
+ static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci)
+ {
+ int i;
+ static int inited;
+
+ if (!inited) {
+ inited = 1;
+ for(i = 0;i < 256; i++)
+ rop_to_index[i] = CIRRUS_ROP_NOP_INDEX; /* nop rop */
+ rop_to_index[CIRRUS_ROP_0] = 0;
+ rop_to_index[CIRRUS_ROP_SRC_AND_DST] = 1;
+ rop_to_index[CIRRUS_ROP_NOP] = 2;
+ rop_to_index[CIRRUS_ROP_SRC_AND_NOTDST] = 3;
+ rop_to_index[CIRRUS_ROP_NOTDST] = 4;
+ rop_to_index[CIRRUS_ROP_SRC] = 5;
+ rop_to_index[CIRRUS_ROP_1] = 6;
+ rop_to_index[CIRRUS_ROP_NOTSRC_AND_DST] = 7;
+ rop_to_index[CIRRUS_ROP_SRC_XOR_DST] = 8;
+ rop_to_index[CIRRUS_ROP_SRC_OR_DST] = 9;
+ rop_to_index[CIRRUS_ROP_NOTSRC_OR_NOTDST] = 10;
+ rop_to_index[CIRRUS_ROP_SRC_NOTXOR_DST] = 11;
+ rop_to_index[CIRRUS_ROP_SRC_OR_NOTDST] = 12;
+ rop_to_index[CIRRUS_ROP_NOTSRC] = 13;
+ rop_to_index[CIRRUS_ROP_NOTSRC_OR_DST] = 14;
+ rop_to_index[CIRRUS_ROP_NOTSRC_AND_NOTDST] = 15;
+ s->device_id = device_id;
+ if (is_pci)
+ s->bustype = CIRRUS_BUSTYPE_PCI;
+ else
+ s->bustype = CIRRUS_BUSTYPE_ISA;
+ }
- register_savevm("cirrus_vga", 0, 2, cirrus_vga_save, cirrus_vga_load, s);
+ register_ioport_write(0x3c0, 16, 1, vga_ioport_write, s);
+
+ register_ioport_write(0x3b4, 2, 1, vga_ioport_write, s);
+ register_ioport_write(0x3d4, 2, 1, vga_ioport_write, s);
+ register_ioport_write(0x3ba, 1, 1, vga_ioport_write, s);
+ register_ioport_write(0x3da, 1, 1, vga_ioport_write, s);
+
+ register_ioport_read(0x3c0, 16, 1, vga_ioport_read, s);
+
+ register_ioport_read(0x3b4, 2, 1, vga_ioport_read, s);
+ register_ioport_read(0x3d4, 2, 1, vga_ioport_read, s);
+ register_ioport_read(0x3ba, 1, 1, vga_ioport_read, s);
+ register_ioport_read(0x3da, 1, 1, vga_ioport_read, s);
+
+ s->vga_io_memory = cpu_register_io_memory(0, cirrus_vga_mem_read,
+ cirrus_vga_mem_write, s);
+ cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000,
+ s->vga_io_memory);
+ qemu_register_coalesced_mmio(isa_mem_base + 0x000a0000, 0x20000);
+
+ qemu_register_reset(cirrus_reset, s);
+ cirrus_reset(s);
+ register_savevm("cirrus_vga", 0, 3, cirrus_vga_save, cirrus_vga_load, s);
}
/***************************************
{
CirrusVGAState *s;
- s = qemu_mallocz(sizeof(CirrusVGAState));
-
+ if (vga_ram_size != 4*1024*1024) {
+ fprintf(stderr, "The -videoram option does not work with the cirrus vga model. Video ram set to 4M. \n");
+ vga_ram_size = 4*1024*1024;
+ }
+
+ s = qemu_mallocz(sizeof(CirrusVGAState));
+
vga_common_init((VGAState *)s,
ds, vga_ram_base, vga_ram_offset, vga_ram_size);
cirrus_init_common(s, CIRRUS_ID_CLGD5430, 0);
/* XXX: add byte swapping apertures */
cpu_register_physical_memory(addr, s->vram_size,
s->cirrus_linear_io_addr);
+ s->lfb_addr = addr;
+ s->lfb_end = addr + s->vram_size;
+
+ if (s->lfb_addr != s->vram_gmfn)
+ fprintf(logfile, "cirrus vga map change while on lfb mode\n");
+
cpu_register_physical_memory(addr + 0x1000000, 0x400000,
s->cirrus_linear_bitblt_io_addr);
+
+ s->map_addr = s->map_end = 0;
+ s->lfb_addr = addr & TARGET_PAGE_MASK;
+ s->lfb_end = ((addr + VGA_RAM_SIZE) + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK;
+ /* account for overflow */
+ if (s->lfb_end < addr + VGA_RAM_SIZE)
+ s->lfb_end = addr + VGA_RAM_SIZE;
}
static void cirrus_pci_mmio_map(PCIDevice *d, int region_num,
pci_conf[0x0a] = PCI_CLASS_SUB_VGA;
pci_conf[0x0b] = PCI_CLASS_BASE_DISPLAY;
pci_conf[0x0e] = PCI_CLASS_HEADERTYPE_00h;
- /* setup VGA */
- s = &d->cirrus_vga;
+ pci_conf[0x2c] = 0x53; /* subsystem vendor: XenSource */
+ pci_conf[0x2d] = 0x58;
+ pci_conf[0x2e] = 0x01; /* subsystem device */
+ pci_conf[0x2f] = 0x00;
+
+ if (vga_ram_size != 4*1024*1024) {
+ fprintf(stderr, "The -videoram option does not work with the cirrus vga model. Video ram set to 4M. \n");
+ vga_ram_size = 4*1024*1024;
+ }
+
+ /* setup VGA */
+ s = &d->cirrus_vga;
vga_common_init((VGAState *)s,
ds, vga_ram_base, vga_ram_offset, vga_ram_size);
cirrus_init_common(s, device_id, 1);
- s->console = graphic_console_init(s->ds, s->update, s->invalidate,
- s->screen_dump, s->text_update, s);
-
+
s->pci_dev = (PCIDevice *)d;
/* setup memory space */
s->media_changed = 0;
}
-
- struct partition {
- uint8_t boot_ind; /* 0x80 - active */
- uint8_t head; /* starting head */
- uint8_t sector; /* starting sector */
- uint8_t cyl; /* starting cylinder */
- uint8_t sys_ind; /* What partition type */
- uint8_t end_head; /* end head */
- uint8_t end_sector; /* end sector */
- uint8_t end_cyl; /* end cylinder */
- uint32_t start_sect; /* starting sector counting from 0 */
- uint32_t nr_sects; /* nr of sectors in partition */
- } __attribute__((packed));
-
- /* try to guess the disk logical geometry from the MSDOS partition table. Return 0 if OK, -1 if could not guess */
- static int guess_disk_lchs(IDEState *s,
- int *pcylinders, int *pheads, int *psectors)
- {
- uint8_t *buf = s->io_buffer;
- int ret, i, heads, sectors, cylinders;
- struct partition *p;
- uint32_t nr_sects;
-
- ret = bdrv_read(s->bs, 0, buf, 1);
- if (ret < 0) {
- return -1;
- }
- /* test msdos magic */
- if (buf[510] != 0x55 || buf[511] != 0xaa) {
- return -1;
- }
- for(i = 0; i < 4; i++) {
- p = ((struct partition *)(buf + 0x1be)) + i;
- nr_sects = le32_to_cpu(p->nr_sects);
- if (nr_sects && p->end_head) {
- /* We make the assumption that the partition terminates on
- a cylinder boundary */
- heads = p->end_head + 1;
- sectors = p->end_sector & 63;
- if (sectors == 0)
- continue;
- cylinders = s->nb_sectors / (heads * sectors);
- if (cylinders < 1 || cylinders > 16383)
- continue;
- *pheads = heads;
- *psectors = sectors;
- *pcylinders = cylinders;
- #if 0
- printf("guessed geometry: LCHS=%d %d %d\n",
- cylinders, heads, sectors);
- #endif
- return 0;
- }
- }
- return -1;
- }
-
+/* Unplug all of the IDE hard disks, starting at index @start in the
+ table. */
+static void _ide_unplug_harddisks(int start)
+{
+ IDEState *s;
+ int i, j;
+
+ if (!principal_ide_controller) {
+ fprintf(stderr, "No principal controller?\n");
+ return;
+ }
+ for (i = start; i < 4; i++) {
+ s = principal_ide_controller->ide_if + i;
+ if (!s->bs)
+ continue; /* drive not present */
+ if (s->is_cdrom)
+ continue; /* cdrom */
+ /* Is a hard disk, unplug it. */
+ for (j = 0; j < nb_drives; j++)
+ if (drives_table[j].bdrv == s->bs)
+ drives_table[j].bdrv = NULL;
+ bdrv_close(s->bs);
+ s->bs = NULL;
+ ide_reset(s);
+ }
+}
+
+/* Unplug all hard disks except for the primary master (which will
+ almost always be the boot device). */
+void ide_unplug_aux_harddisks(void)
+{
+ _ide_unplug_harddisks(1);
+}
+
+/* Unplug all hard disks, including the boot device. */
+void ide_unplug_harddisks(void)
+{
+ _ide_unplug_harddisks(0);
+}
+
static void ide_init2(IDEState *ide_state,
BlockDriverState *hd0, BlockDriverState *hd1,
qemu_irq irq)
#define IOPTE_VALID 0x00000002 /* IOPTE is valid */
#define IOPTE_WAZ 0x00000001 /* Write as zeros */
- #define PAGE_SHIFT 12
+#if defined(__i386__) || defined(__x86_64__)
- #define PAGE_SHIFT 14
+ #define IOMMU_PAGE_SHIFT 12
+#elif defined(__ia64__)
- #define PAGE_SIZE (1 << PAGE_SHIFT)
- #define PAGE_MASK (PAGE_SIZE - 1)
++#define IOMMU_PAGE_SHIFT 14
+#endif
+ #define IOMMU_PAGE_SIZE (1 << IOMMU_PAGE_SHIFT)
+ #define IOMMU_PAGE_MASK ~(IOMMU_PAGE_SIZE - 1)
typedef struct IOMMUState {
- target_phys_addr_t addr;
uint32_t regs[IOMMU_NREGS];
target_phys_addr_t iostart;
uint32_t version;
shift = (offset & 3) * 8;
return (s->scratch[n] >> shift) & 0xff;
}
- BADF("readb 0x%x\n", offset);
- exit(1);
+ /* XEN: This path can be triggered (e.g. ASPI8DOS.SYS reads 0x8). */
+ return 0;
+ #undef CASE_GET_REG24
#undef CASE_GET_REG32
}
.name = "mipssim",
.desc = "MIPS MIPSsim platform",
.init = mips_mipssim_init,
- .ram_require = BIOS_SIZE + VGA_RAM_SIZE /* unused */,
+ .ram_require = BIOS_SIZE /* unused */,
.nodisk_ok = 1,
- .max_cpus = 1,
};
.name = "mips",
.desc = "mips r4k platform",
.init = mips_r4k_init,
- .ram_require = VGA_RAM_SIZE + BIOS_SIZE,
+ .ram_require = BIOS_SIZE,
.nodisk_ok = 1,
- .max_cpus = 1,
};
ne2000_reset(s);
- s->vc = qemu_new_vlan_client(nd->vlan, ne2000_receive,
- ne2000_can_receive, s);
-
- snprintf(s->vc->info_str, sizeof(s->vc->info_str),
- "ne2000 macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
- s->macaddr[0],
- s->macaddr[1],
- s->macaddr[2],
- s->macaddr[3],
- s->macaddr[4],
- s->macaddr[5]);
-
+ s->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name,
+ ne2000_receive, ne2000_can_receive, s);
+
+ qemu_format_nic_info_str(s->vc, s->macaddr);
+
- register_savevm("ne2000", -1, 2, ne2000_save, ne2000_load, s);
+ register_savevm("ne2000", base, 2, ne2000_save, ne2000_load, s);
}
/***********************************************************/
#include "smbus.h"
#include "boards.h"
#include "console.h"
+#include "exec-all.h"
+
+#include "xen_platform.h"
#include "fw_cfg.h"
+ #include "virtio-blk.h"
+ #include "virtio-balloon.h"
+ #include "virtio-console.h"
+ #include "hpet_emul.h"
/* output Bochs bios info messages */
//#define DEBUG_BIOS
memset(gpr, 0, sizeof gpr);
gpr[4] = cmdline_addr-real_addr-16; /* SP (-16 is paranoia) */
- generate_bootsect(gpr, seg, 0);
+ generate_bootsect(option_rom, gpr, seg, 0);
+#endif
}
static void main_cpu_reset(void *opaque)
exit(1);
}
- /* VGA BIOS load */
- if (cirrus_vga_enabled) {
- snprintf(buf, sizeof(buf), "%s/%s", bios_dir, VGABIOS_CIRRUS_FILENAME);
- } else {
- snprintf(buf, sizeof(buf), "%s/%s", bios_dir, VGABIOS_FILENAME);
- }
- vga_bios_size = get_image_size(buf);
- if (vga_bios_size <= 0 || vga_bios_size > 65536)
- goto vga_bios_error;
- vga_bios_offset = qemu_ram_alloc(65536);
-
- ret = load_image_targphys(buf, vga_bios_offset, vga_bios_size);
- if (ret != vga_bios_size) {
- vga_bios_error:
- fprintf(stderr, "qemu: could not load VGA BIOS '%s'\n", buf);
- exit(1);
+ if (cirrus_vga_enabled || std_vga_enabled || vmsvga_enabled) {
+ /* VGA BIOS load */
+ if (cirrus_vga_enabled) {
+ snprintf(buf, sizeof(buf), "%s/%s", bios_dir, VGABIOS_CIRRUS_FILENAME);
+ } else {
+ snprintf(buf, sizeof(buf), "%s/%s", bios_dir, VGABIOS_FILENAME);
+ }
+ vga_bios_size = get_image_size(buf);
+ if (vga_bios_size <= 0 || vga_bios_size > 65536)
+ goto vga_bios_error;
+ vga_bios_offset = qemu_ram_alloc(65536);
+
- ret = load_image(buf, phys_ram_base + vga_bios_offset);
++ ret = load_image_targphys(buf, phys_ram_base + vga_bios_offset);
+ if (ret != vga_bios_size) {
+ vga_bios_error:
+ fprintf(stderr, "qemu: could not load VGA BIOS '%s'\n", buf);
+ exit(1);
+ }
}
/* setup basic memory access */
}
}
+ if (pci_enabled) {
+ PCI_EMULATION_INFO *p;
+ for (p = PciEmulationInfoHead; p != NULL; p = p->next) {
+ pci_emulation_init(pci_bus, p);
+ }
+ }
++
+ /* Add virtio block devices */
+ if (pci_enabled) {
+ int index;
+ int unit_id = 0;
+
+ while ((index = drive_get_index(IF_VIRTIO, 0, unit_id)) != -1) {
+ virtio_blk_init(pci_bus, drives_table[index].bdrv);
+ unit_id++;
+ }
+ }
+
+ /* Add virtio balloon device */
+ if (pci_enabled)
+ virtio_balloon_init(pci_bus);
+
+ /* Add virtio console devices */
+ if (pci_enabled) {
+ for(i = 0; i < MAX_VIRTIO_CONSOLES; i++) {
+ if (virtcon_hds[i])
+ virtio_console_init(pci_bus, virtcon_hds[i]);
+ }
+ }
}
static void pc_init_pci(ram_addr_t ram_size, int vga_ram_size,
{
pc_init1(ram_size, vga_ram_size, boot_device, ds,
kernel_filename, kernel_cmdline,
- initrd_filename, 0, cpu_model);
+ initrd_filename, 0, cpu_model,
+ direct_pci);
+}
+
+/* set CMOS shutdown status register (index 0xF) as S3_resume(0xFE)
+ BIOS will read it and start S3 resume at POST Entry*/
+void cmos_set_s3_resume(void)
+{
+ if (rtc_state)
+ rtc_set_memory(rtc_state, 0xF, 0xFE);
}
+ /* set CMOS shutdown status register (index 0xF) as S3_resume(0xFE)
+ BIOS will read it and start S3 resume at POST Entry */
+ void cmos_set_s3_resume(void)
+ {
+ if (rtc_state)
+ rtc_set_memory(rtc_state, 0xF, 0xFE);
+ }
+
QEMUMachine pc_machine = {
.name = "pc",
.desc = "Standard PC",
void piix4_smbus_register_device(SMBusDevice *dev, uint8_t addr);
void acpi_bios_init(void);
+void acpi_php_add(int);
+void acpi_php_del(int);
+
+ /* hpet.c */
+ extern int no_hpet;
+
/* pcspk.c */
void pcspk_init(PITState *);
int pcspk_audio_init(AudioState *, qemu_irq *pic);
#include "pci.h"
#include "console.h"
#include "net.h"
+ #include "virtio-net.h"
+#include "exec-all.h"
+#include "qemu-xen.h"
+
//#define DEBUG_PCI
struct PCIBus {
static void pci_set_irq(void *opaque, int irq_num, int level);
target_phys_addr_t pci_mem_base;
-static int pci_irq_index;
+ static uint16_t pci_default_sub_vendor_id = PCI_SUBVENDOR_ID_REDHAT_QUMRANET;
+ static uint16_t pci_default_sub_device_id = PCI_SUBDEVICE_ID_QEMU;
static PCIBus *first_bus;
static int pcibus_load(QEMUFile *f, void *opaque, int version_id)
pci_for_each_device(0, pci_info_device);
}
+ static const char * const pci_nic_models[] = {
+ "ne2k_pci",
+ "i82551",
+ "i82557b",
+ "i82559er",
+ "rtl8139",
++ "e100",
+ "e1000",
+ "pcnet",
+ "virtio",
+ NULL
+ };
+
+ typedef void (*PCINICInitFn)(PCIBus *, NICInfo *, int);
+
+ static PCINICInitFn pci_nic_init_fns[] = {
+ pci_ne2000_init,
+ pci_i82551_init,
+ pci_i82557b_init,
+ pci_i82559er_init,
+ pci_rtl8139_init,
++ pci_e100_init,
+ pci_e1000_init,
+ pci_pcnet_init,
+ virtio_net_init,
+ NULL
+ };
+
/* Initialize a PCI NIC. */
- void pci_nic_init(PCIBus *bus, NICInfo *nd, int devfn)
+ void pci_nic_init(PCIBus *bus, NICInfo *nd, int devfn,
+ const char *default_model)
{
- if (strcmp(nd->model, "ne2k_pci") == 0) {
- pci_ne2000_init(bus, nd, devfn);
- } else if (strcmp(nd->model, "i82551") == 0) {
- pci_i82551_init(bus, nd, devfn);
- } else if (strcmp(nd->model, "i82557b") == 0) {
- pci_i82557b_init(bus, nd, devfn);
- } else if (strcmp(nd->model, "i82559er") == 0) {
- pci_i82559er_init(bus, nd, devfn);
- } else if (strcmp(nd->model, "rtl8139") == 0) {
- pci_rtl8139_init(bus, nd, devfn);
- } else if (strcmp(nd->model, "e1000") == 0) {
- pci_e1000_init(bus, nd, devfn);
- } else if (strcmp(nd->model, "pcnet") == 0) {
- pci_pcnet_init(bus, nd, devfn);
- } else if (strcmp(nd->model, "?") == 0) {
- fprintf(stderr, "qemu: Supported PCI NICs: i82551 i82557b i82559er"
- " ne2k_pci pcnet rtl8139 e100 e1000\n");
- exit (1);
- } else {
- fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd->model);
- exit (1);
- }
+ int i;
+
+ qemu_check_nic_model_list(nd, pci_nic_models, default_model);
+
+ for (i = 0; pci_nic_models[i]; i++)
+ if (strcmp(nd->model, pci_nic_models[i]) == 0)
+ pci_nic_init_fns[i](bus, nd, devfn);
}
+void pci_unplug_netifs(void)
+{
+ PCIBus *bus;
+ PCIDevice *dev;
+ PCIIORegion *region;
+ int x;
+ int i;
+
+ /* We only support one PCI bus */
+ for (bus = first_bus; bus; bus = NULL) {
+ for (x = 0; x < 256; x++) {
+ dev = bus->devices[x];
+ if (dev &&
+ dev->config[0xa] == 0 &&
+ dev->config[0xb] == 2) {
+ /* Found a netif. Remove it from the bus. Note that
+ we don't free it here, since there could still be
+ references to it floating around. There are only
+ ever one or two structures leaked, and it's not
+ worth finding them all. */
+ bus->devices[x] = NULL;
+ for (i = 0; i < PCI_NUM_REGIONS; i++) {
+ region = &dev->io_regions[i];
+ if (region->addr == (uint32_t)-1 ||
+ region->size == 0)
+ continue;
+ fprintf(logfile, "region type %d at [%x,%x).\n",
+ region->type, region->addr,
+ region->addr+region->size);
+ if (region->type == PCI_ADDRESS_SPACE_IO) {
+ isa_unassign_ioport(region->addr, region->size);
+ } else if (region->type == PCI_ADDRESS_SPACE_MEM) {
+ unregister_iomem(region->addr);
+ }
+ }
+ }
+ }
+ }
+}
+
typedef struct {
PCIDevice dev;
PCIBus *bus;
PCIBus *pci_prep_init(qemu_irq *pic);
/* apb_pci.c */
- PCIBus *pci_apb_init(target_phys_addr_t special_base, target_phys_addr_t mem_base,
- qemu_irq *pic);
+ PCIBus *pci_apb_init(target_phys_addr_t special_base,
+ target_phys_addr_t mem_base,
+ qemu_irq *pic, PCIBus **bus2, PCIBus **bus3);
+
+ /* sh_pci.c */
+ PCIBus *sh_pci_register_bus(pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
+ qemu_irq *pic, int devfn_min, int nirq);
+/* pass-through.c */
+int pt_init(PCIBus *e_bus, const char *direct_pci_opt);
+
#endif
s->cplus_txbuffer_len = 0;
s->cplus_txbuffer_offset = 0;
- register_savevm("rtl8139", -1, 4, rtl8139_save, rtl8139_load, s);
+ instance = pci_bus_num(bus) << 8 | s->pci_dev->devfn;
- register_savevm("rtl8139", instance, 3, rtl8139_save, rtl8139_load, s);
++ register_savevm("rtl8139", instance, 4, rtl8139_save, rtl8139_load, s);
#ifdef RTL8139_ONBOARD_TIMER
s->timer = qemu_new_timer(vm_clock, rtl8139_timer, s);
#define STATUS_GOOD 0
#define STATUS_CHECK_CONDITION 2
+#ifdef CONFIG_STUBDOM
+#include <xen/io/blkif.h>
+#define SCSI_DMA_BUF_SIZE ((BLKIF_MAX_SEGMENTS_PER_REQUEST - 1) * PAGE_SIZE)
+#else
#define SCSI_DMA_BUF_SIZE 131072
+#endif
+
+ #define SCSI_MAX_INQUIRY_LEN 256
+
typedef struct SCSIRequest {
SCSIDeviceState *dev;
uint32_t tag;
.name = "sun4u",
.desc = "Sun4u platform",
.init = sun4u_init,
- .ram_require = PROM_SIZE_MAX + VGA_RAM_SIZE,
+ .ram_require = PROM_SIZE_MAX,
.nodisk_ok = 1,
- .max_cpus = 16,
+ .max_cpus = 1, // XXX for now
};
QEMUMachine sun4v_machine = {
.name = "sun4v",
.desc = "Sun4v platform",
.init = sun4v_init,
- .ram_require = PROM_SIZE_MAX + VGA_RAM_SIZE,
+ .ram_require = PROM_SIZE_MAX,
.nodisk_ok = 1,
- .max_cpus = 16,
+ .max_cpus = 1, // XXX for now
};
QEMUMachine niagara_machine = {
.name = "Niagara",
.desc = "Sun4v platform, Niagara",
.init = niagara_init,
- .ram_require = PROM_SIZE_MAX + VGA_RAM_SIZE,
+ .ram_require = PROM_SIZE_MAX,
.nodisk_ok = 1,
- .max_cpus = 16,
+ .max_cpus = 1, // XXX for now
};
};
int kind;
int protocol;
- int idle;
+ uint8_t idle;
- int changed;
void *datain_opaque;
void (*datain)(void *);
} USBHIDState;
case 8:
return 0;
case 15:
- if (s->bgr)
- return 5;
- else
- return 1;
+ return 1;
case 16:
- if (s->bgr)
- return 6;
- else
- return 2;
+ return 2;
case 32:
-- if (s->bgr)
-- return 4;
-- else
-- return 3;
++ return 3;
}
}
vga_draw_glyph8_func *vga_draw_glyph8;
vga_draw_glyph9_func *vga_draw_glyph9;
- vga_dirty_log_stop(s);
+ /* Disable dirty bit tracking */
+ xc_hvm_track_dirty_vram(xc_handle, domid, 0, 0, NULL);
+
+ /* total width & height */
+ cheight = (s->cr[9] & 0x1f) + 1;
+ cw = 8;
+ if (!(s->sr[1] & 0x01))
+ cw = 9;
+ if (s->sr[1] & 0x08)
+ cw = 16; /* NOTE: no 18 pixel wide */
+ width = (s->cr[0x01] + 1);
+ if (s->cr[0x06] == 100) {
+ /* ugly hack for CGA 160x100x16 - explain me the logic */
+ height = 100;
+ } else {
+ height = s->cr[0x12] |
+ ((s->cr[0x07] & 0x02) << 7) |
+ ((s->cr[0x07] & 0x40) << 3);
+ height = (height + 1) / cheight;
+ }
+ if ((height * width) > CH_ATTR_SIZE) {
+ /* better than nothing: exit if transient size is too big */
+ return;
+ }
+
- s->last_scr_width = width * cw;
- s->last_scr_height = height * cheight;
+ if (width != s->last_width || height != s->last_height ||
+ cw != s->last_cw || cheight != s->last_ch || s->last_depth) {
- dpy_resize(s->ds, s->last_scr_width, s->last_scr_height);
++ s->last_scr_width = width * cw;
++ s->last_scr_height = height * cheight;
++ qemu_console_resize(s->ds, s->last_scr_width, s->last_scr_height);
+ s->last_depth = 0;
++ s->last_width = width;
++ s->last_height = height;
++ s->last_ch = cheight;
++ s->last_cw = cw;
+ full_update = 1;
+ }
- s->last_width = width;
- s->last_height = height;
- s->last_ch = cheight;
- s->last_cw = cw;
+
+ s->rgb_to_pixel =
+ rgb_to_pixel_dup_table[get_depth_index(s->ds)];
full_update |= update_palette16(s);
palette = s->last_palette;
-
+
- x_incr = cw * ((s->ds->depth + 7) >> 3);
++ x_incr = cw * ds_get_bytes_per_pixel(s->ds);
/* compute font data address (in plane 2) */
v = s->sr[3];
offset = (((v >> 4) & 1) | ((v << 1) & 6)) * 8192 * 4 + 2;
line_offset = s->line_offset;
s1 = s->vram_ptr + (s->start_addr * 4);
- vga_get_text_resolution(s, &width, &height, &cw, &cheight);
- x_incr = cw * ((ds_get_bits_per_pixel(s->ds) + 7) >> 3);
+ /* total width & height */
+ cheight = (s->cr[9] & 0x1f) + 1;
+ cw = 8;
+ if (!(s->sr[1] & 0x01))
+ cw = 9;
+ if (s->sr[1] & 0x08)
+ cw = 16; /* NOTE: no 18 pixel wide */
- x_incr = cw * ((s->ds->depth + 7) >> 3);
++ x_incr = cw * ds_get_bytes_per_pixel(s->ds);
+ width = (s->cr[0x01] + 1);
+ if (s->cr[0x06] == 100) {
+ /* ugly hack for CGA 160x100x16 - explain me the logic */
+ height = 100;
+ } else {
+ height = s->cr[0x12] |
+ ((s->cr[0x07] & 0x02) << 7) |
+ ((s->cr[0x07] & 0x40) << 3);
+ height = (height + 1) / cheight;
+ }
if ((height * width) > CH_ATTR_SIZE) {
/* better than nothing: exit if transient size is too big */
return;
cw != s->last_cw || cheight != s->last_ch) {
s->last_scr_width = width * cw;
s->last_scr_height = height * cheight;
-- qemu_console_resize(s->console, s->last_scr_width, s->last_scr_height);
++ qemu_console_resize(s->ds, s->last_scr_width, s->last_scr_height);
s->last_width = width;
s->last_height = height;
s->last_ch = cheight;
s->shift_control = shift_control;
s->double_scan = double_scan;
}
- ds_depth = s->ds->depth;
+ if (shift_control == 1 && (s->sr[0x01] & 8)) {
+ disp_width <<= 1;
+ }
+
- if (s->ds->dpy_resize_shared) {
- if (s->line_offset != s->last_line_offset ||
- disp_width != s->last_width ||
- height != s->last_height ||
- s->last_depth != depth) {
- dpy_resize_shared(s->ds, disp_width, height, depth, s->line_offset, s->vram_ptr + (s->start_addr * 4));
- s->last_scr_width = disp_width;
- s->last_scr_height = height;
- s->last_width = disp_width;
- s->last_height = height;
- s->last_line_offset = s->line_offset;
- s->last_depth = depth;
- full_update = 1;
- } else if (s->ds->shared_buf && (full_update || s->ds->data != s->vram_ptr + (s->start_addr * 4)))
- s->ds->dpy_setdata(s->ds, s->vram_ptr + (s->start_addr * 4));
- } else if (disp_width != s->last_width ||
- height != s->last_height) {
- dpy_resize(s->ds, disp_width, height);
++ ds_depth = ds_get_bits_per_pixel(s->ds);
+ depth = s->get_bpp(s);
++ if (s->line_offset != s->last_line_offset ||
++ disp_width != s->last_width ||
++ height != s->last_height ||
++ s->last_depth != depth) {
++#if defined(WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN)
++ if (depth == 16 || depth == 32) {
++#else
++ if (depth == 32) {
++#endif
++ if (is_graphic_console()) {
++ qemu_free_displaysurface(s->ds);
++ s->ds->surface = qemu_create_displaysurface_from(disp_width, height, depth,
++ s->line_offset,
++ s->vram_ptr + (s->start_addr * 4));
++#if defined(WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
++ s->ds->surface->pf = qemu_different_endianness_pixelformat(depth);
++#endif
++ dpy_resize(s->ds);
++ } else {
++ qemu_console_resize(s->ds, disp_width, height);
++ }
++ } else {
++ qemu_console_resize(s->ds, disp_width, height);
++ }
+ s->last_scr_width = disp_width;
+ s->last_scr_height = height;
+ s->last_width = disp_width;
+ s->last_height = height;
++ s->last_line_offset = s->line_offset;
++ s->last_depth = depth;
+ full_update = 1;
++ } else if (is_graphic_console() && is_buffer_shared(s->ds->surface) &&
++ (full_update || s->ds->surface->data != s->vram_ptr + (s->start_addr * 4))) {
++ s->ds->surface->data = s->vram_ptr + (s->start_addr * 4);
++ dpy_setdata(s->ds);
+ }
+
+ s->rgb_to_pixel =
+ rgb_to_pixel_dup_table[get_depth_index(s->ds)];
if (shift_control == 0) {
full_update |= update_palette16(s);
break;
}
}
- vga_draw_line = vga_draw_line_table[v * NB_DEPTHS + get_depth_index(s->ds)];
- if (disp_width != s->last_width ||
- height != s->last_height) {
- qemu_console_resize(s->console, disp_width, height);
- s->last_scr_width = disp_width;
- s->last_scr_height = height;
- s->last_width = disp_width;
- s->last_height = height;
- full_update = 1;
- }
- if (s->cursor_invalidate)
+ vga_draw_line = vga_draw_line_table[v * NB_DEPTHS + get_depth_index(s->ds)];
- if (!s->ds->shared_buf && s->cursor_invalidate)
++ if (!is_buffer_shared(s->ds->surface) && s->cursor_invalidate)
s->cursor_invalidate(s);
line_offset = s->line_offset;
addr1 = (s->start_addr * 4);
bwidth = (width * bits + 7) / 8;
y_start = -1;
- page_min = 0x7fffffff;
- page_max = -1;
+ page_min = 0;
+ page_max = 0;
- d = s->ds->data;
- linesize = s->ds->linesize;
+ d = ds_get_data(s->ds);
+ linesize = ds_get_linesize(s->ds);
y1 = 0;
for(y = 0; y < height; y++) {
addr = addr1;
if (update) {
if (y_start < 0)
y_start = y;
- if (page0 < page_min)
+ if (page_min == 0 || page0 < page_min)
page_min = page0;
- if (page1 > page_max)
+ if (page_max == 0 || page1 > page_max)
page_max = page1;
- if (!s->ds->shared_buf) {
- vga_draw_line(s, d, s->vram_ptr + addr, width);
- if (s->cursor_draw_line)
- s->cursor_draw_line(s, d, y);
++ if (!is_buffer_shared(s->ds->surface)) {
+ vga_draw_line(s, d, s->vram_ptr + addr, width);
+ if (s->cursor_draw_line)
+ s->cursor_draw_line(s, d, y);
+ }
} else {
if (y_start >= 0) {
/* flush to display */
return;
if (s->last_scr_width <= 0 || s->last_scr_height <= 0)
return;
- vga_dirty_log_stop(s);
- if (s->ds->depth == 8)
+ /* Disable dirty bit tracking */
+ xc_hvm_track_dirty_vram(xc_handle, domid, 0, 0, NULL);
+
+ s->rgb_to_pixel =
+ rgb_to_pixel_dup_table[get_depth_index(s->ds)];
+ if (ds_get_bits_per_pixel(s->ds) == 8)
val = s->rgb_to_pixel(0, 0, 0);
else
val = 0;
- w = s->last_scr_width * ((s->ds->depth + 7) >> 3);
- d = s->ds->data;
- w = s->last_scr_width * ((ds_get_bits_per_pixel(s->ds) + 7) >> 3);
++ w = s->last_scr_width * ds_get_bytes_per_pixel(s->ds);
+ d = ds_get_data(s->ds);
for(i = 0; i < s->last_scr_height; i++) {
memset(d, val, w);
- d += s->ds->linesize;
+ d += ds_get_linesize(s->ds);
}
dpy_update(s->ds, 0, 0,
s->last_scr_width, s->last_scr_height);
VGAState *s = (VGAState *)opaque;
int full_update, graphic_mode;
- if (s->ds->depth == 0) {
+ if (ds_get_bits_per_pixel(s->ds) == 0) {
/* nothing to do */
} else {
- s->rgb_to_pixel =
- rgb_to_pixel_dup_table[get_depth_index(s->ds)];
-
full_update = 0;
if (!(s->ar_index & 0x20)) {
graphic_mode = GMODE_BLANK;
cw != s->last_cw || cheight != s->last_ch) {
s->last_scr_width = width * cw;
s->last_scr_height = height * cheight;
-- qemu_console_resize(s->console, width, height);
++ s->ds->surface->width = width;
++ s->ds->surface->height = height;
++ dpy_resize(s->ds);
s->last_width = width;
s->last_height = height;
s->last_ch = cheight;
s->last_width = 60;
s->last_height = height = 3;
dpy_cursor(s->ds, -1, -1);
-- qemu_console_resize(s->console, s->last_width, height);
++ s->ds->surface->width = s->last_width;
++ s->ds->surface->height = height;
++ dpy_resize(s->ds);
for (dst = chardata, i = 0; i < s->last_width * height; i ++)
console_write_ch(dst ++, ' ');
}
}
-void vga_dirty_log_stop(VGAState *s)
+/* do the same job as vgabios before vgabios get ready - yeah */
+void vga_bios_init(VGAState *s)
+{
+ uint8_t palette_model[192] = {
+ 0, 0, 0, 0, 0, 170, 0, 170,
+ 0, 0, 170, 170, 170, 0, 0, 170,
+ 0, 170, 170, 85, 0, 170, 170, 170,
+ 85, 85, 85, 85, 85, 255, 85, 255,
+ 85, 85, 255, 255, 255, 85, 85, 255,
+ 85, 255, 255, 255, 85, 255, 255, 255,
+ 0, 21, 0, 0, 21, 42, 0, 63,
+ 0, 0, 63, 42, 42, 21, 0, 42,
+ 21, 42, 42, 63, 0, 42, 63, 42,
+ 0, 21, 21, 0, 21, 63, 0, 63,
+ 21, 0, 63, 63, 42, 21, 21, 42,
+ 21, 63, 42, 63, 21, 42, 63, 63,
+ 21, 0, 0, 21, 0, 42, 21, 42,
+ 0, 21, 42, 42, 63, 0, 0, 63,
+ 0, 42, 63, 42, 0, 63, 42, 42,
+ 21, 0, 21, 21, 0, 63, 21, 42,
+ 21, 21, 42, 63, 63, 0, 21, 63,
+ 0, 63, 63, 42, 21, 63, 42, 63,
+ 21, 21, 0, 21, 21, 42, 21, 63,
+ 0, 21, 63, 42, 63, 21, 0, 63,
+ 21, 42, 63, 63, 0, 63, 63, 42,
+ 21, 21, 21, 21, 21, 63, 21, 63,
+ 21, 21, 63, 63, 63, 21, 21, 63,
+ 21, 63, 63, 63, 21, 63, 63, 63
+ };
+
+ s->latch = 0;
+
+ s->sr_index = 3;
+ s->sr[0] = 3;
+ s->sr[1] = 0;
+ s->sr[2] = 3;
+ s->sr[3] = 0;
+ s->sr[4] = 2;
+ s->sr[5] = 0;
+ s->sr[6] = 0;
+ s->sr[7] = 0;
+
+ s->gr_index = 5;
+ s->gr[0] = 0;
+ s->gr[1] = 0;
+ s->gr[2] = 0;
+ s->gr[3] = 0;
+ s->gr[4] = 0;
+ s->gr[5] = 16;
+ s->gr[6] = 14;
+ s->gr[7] = 15;
+ s->gr[8] = 255;
+
+ /* changed by out 0x03c0 */
+ s->ar_index = 32;
+ s->ar[0] = 0;
+ s->ar[1] = 1;
+ s->ar[2] = 2;
+ s->ar[3] = 3;
+ s->ar[4] = 4;
+ s->ar[5] = 5;
+ s->ar[6] = 6;
+ s->ar[7] = 7;
+ s->ar[8] = 8;
+ s->ar[9] = 9;
+ s->ar[10] = 10;
+ s->ar[11] = 11;
+ s->ar[12] = 12;
+ s->ar[13] = 13;
+ s->ar[14] = 14;
+ s->ar[15] = 15;
+ s->ar[16] = 12;
+ s->ar[17] = 0;
+ s->ar[18] = 15;
+ s->ar[19] = 8;
+ s->ar[20] = 0;
+
+ s->ar_flip_flop = 1;
+
+ s->cr_index = 15;
+ s->cr[0] = 95;
+ s->cr[1] = 79;
+ s->cr[2] = 80;
+ s->cr[3] = 130;
+ s->cr[4] = 85;
+ s->cr[5] = 129;
+ s->cr[6] = 191;
+ s->cr[7] = 31;
+ s->cr[8] = 0;
+ s->cr[9] = 79;
+ s->cr[10] = 14;
+ s->cr[11] = 15;
+ s->cr[12] = 0;
+ s->cr[13] = 0;
+ s->cr[14] = 5;
+ s->cr[15] = 160;
+ s->cr[16] = 156;
+ s->cr[17] = 142;
+ s->cr[18] = 143;
+ s->cr[19] = 40;
+ s->cr[20] = 31;
+ s->cr[21] = 150;
+ s->cr[22] = 185;
+ s->cr[23] = 163;
+ s->cr[24] = 255;
+
+ s->msr = 103;
+ s->fcr = 0;
+ s->st00 = 0;
+ s->st01 = 0;
+
+ /* dac_* & palette will be initialized by os through out 0x03c8 &
+ * out 0c03c9(1:3) */
+ s->dac_state = 0;
+ s->dac_sub_index = 0;
+ s->dac_read_index = 0;
+ s->dac_write_index = 16;
+ s->dac_cache[0] = 255;
+ s->dac_cache[1] = 255;
+ s->dac_cache[2] = 255;
+
+ /* palette */
+ memcpy(s->palette, palette_model, 192);
+
+ s->bank_offset = 0;
+ s->graphic_mode = -1;
+
+ /* TODO: add vbe support if enabled */
+}
+
+
+static VGAState *xen_vga_state;
+
+/* Allocate video memory in the GPFN space */
+void xen_vga_populate_vram(uint64_t vram_addr, uint32_t vga_ram_size)
{
- if (kvm_enabled() && s->map_addr)
- kvm_log_stop(s->map_addr, s->map_end - s->map_addr);
+ unsigned long nr_pfn;
+ xen_pfn_t *pfn_list;
+ int i;
+ int rc;
+
+ fprintf(logfile, "populating video RAM at %llx\n",
+ (unsigned long long)vram_addr);
+
+ nr_pfn = vga_ram_size >> TARGET_PAGE_BITS;
- if (kvm_enabled() && s->lfb_vram_mapped) {
- kvm_log_stop(isa_mem_base + 0xa0000, 0x8000);
- kvm_log_stop(isa_mem_base + 0xa8000, 0x8000);
+ pfn_list = malloc(sizeof(*pfn_list) * nr_pfn);
+
+ for (i = 0; i < nr_pfn; i++)
+ pfn_list[i] = (vram_addr >> TARGET_PAGE_BITS) + i;
+
+ if (xc_domain_memory_populate_physmap(xc_handle, domid, nr_pfn, 0, 0, pfn_list)) {
+ fprintf(stderr, "Failed to populate video ram\n");
+ exit(1);
}
+ free(pfn_list);
+
+ xen_vga_vram_map(vram_addr, vga_ram_size);
+
+ /* Win2K seems to assume that the pattern buffer is at 0xff
+ initially ! */
+ memset(xen_vga_state->vram_ptr, 0xff, vga_ram_size);
}
-static void vga_map(PCIDevice *pci_dev, int region_num,
- uint32_t addr, uint32_t size, int type)
+/* Mapping the video memory from GPFN space */
+void xen_vga_vram_map(uint64_t vram_addr, uint32_t vga_ram_size)
{
- PCIVGAState *d = (PCIVGAState *)pci_dev;
- VGAState *s = &d->vga_state;
- if (region_num == PCI_ROM_SLOT) {
- cpu_register_physical_memory(addr, s->bios_size, s->bios_offset);
- } else {
- cpu_register_physical_memory(addr, s->vram_size, s->vram_offset);
- }
+ unsigned long nr_pfn;
+ xen_pfn_t *pfn_list;
+ int i;
+ void *vram;
+
+ fprintf(logfile, "mapping video RAM from %llx\n",
+ (unsigned long long)vram_addr);
+
+ nr_pfn = vga_ram_size >> TARGET_PAGE_BITS;
+
+ pfn_list = malloc(sizeof(*pfn_list) * nr_pfn);
+
+ for (i = 0; i < nr_pfn; i++)
+ pfn_list[i] = (vram_addr >> TARGET_PAGE_BITS) + i;
- s->map_addr = addr;
- s->map_end = addr + VGA_RAM_SIZE;
+ vram = xc_map_foreign_pages(xc_handle, domid,
+ PROT_READ|PROT_WRITE,
+ pfn_list, nr_pfn);
- vga_dirty_log_start(s);
+ if (!vram) {
+ fprintf(stderr, "Failed to map vram nr_pfn=0x%lx vram_addr=%llx: %s\n",
+ nr_pfn, (unsigned long long)vram_addr, strerror(errno));
+ exit(1);
+ }
+
+ xen_vga_state->vram_ptr = vram;
+#ifdef CONFIG_STUBDOM
- xenfb_pv_display_start(vram);
++ xenfb_pv_display_vram(vram);
+#endif
}
-void vga_common_init(VGAState *s, DisplayState *ds, uint8_t *vga_ram_base,
- ram_addr_t vga_ram_offset, int vga_ram_size)
+/* when used on xen environment, the vga_ram_base is not used */
- void vga_common_init(VGAState *s, DisplayState *ds, uint8_t *vga_ram_base,
++void vga_common_init(VGAState *s, uint8_t *vga_ram_base,
+ unsigned long vga_ram_offset, int vga_ram_size)
{
int i, j, v, b;
expand4to8[i] = v;
}
- s->vram_ptr = vga_ram_base;
+ vga_reset(s);
+
+ xen_vga_state = s;
s->vram_offset = vga_ram_offset;
s->vram_size = vga_ram_size;
-- s->ds = ds;
- ds->palette = s->last_palette;
s->get_bpp = vga_get_bpp;
s->get_offsets = vga_get_offsets;
s->get_resolution = vga_get_resolution;
- s->update = vga_update_display;
- s->invalidate = vga_invalidate_display;
- s->screen_dump = vga_screen_dump;
- s->text_update = vga_update_text;
+
++ s->ds = graphic_console_init(vga_update_display, vga_invalidate_display,
++ vga_screen_dump, vga_update_text, s);
++
+ if (!restore) {
+ xen_vga_populate_vram(VRAM_RESERVED_ADDRESS, s->vram_size);
+ s->vram_gmfn = VRAM_RESERVED_ADDRESS;
+ }
+
- graphic_console_init(s->ds, vga_update_display, vga_invalidate_display,
- vga_screen_dump, vga_update_text, s);
-
+ vga_bios_init(s);
switch (vga_retrace_method) {
case VGA_RETRACE_DUMB:
s->retrace = vga_dumb_retrace;
vga_io_memory = cpu_register_io_memory(0, vga_mem_read, vga_mem_write, s);
cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000,
vga_io_memory);
- qemu_register_coalesced_mmio(isa_mem_base + 0x000a0000, 0x20000);
-}
-
-/* Memory mapped interface */
-static uint32_t vga_mm_readb (void *opaque, target_phys_addr_t addr)
-{
- VGAState *s = opaque;
-
- return vga_ioport_read(s, addr >> s->it_shift) & 0xff;
-}
-
-static void vga_mm_writeb (void *opaque,
- target_phys_addr_t addr, uint32_t value)
-{
- VGAState *s = opaque;
-
- vga_ioport_write(s, addr >> s->it_shift, value & 0xff);
-}
-
-static uint32_t vga_mm_readw (void *opaque, target_phys_addr_t addr)
-{
- VGAState *s = opaque;
-
- return vga_ioport_read(s, addr >> s->it_shift) & 0xffff;
-}
-
-static void vga_mm_writew (void *opaque,
- target_phys_addr_t addr, uint32_t value)
-{
- VGAState *s = opaque;
-
- vga_ioport_write(s, addr >> s->it_shift, value & 0xffff);
-}
-
-static uint32_t vga_mm_readl (void *opaque, target_phys_addr_t addr)
-{
- VGAState *s = opaque;
-
- return vga_ioport_read(s, addr >> s->it_shift);
-}
-
-static void vga_mm_writel (void *opaque,
- target_phys_addr_t addr, uint32_t value)
-{
- VGAState *s = opaque;
-
- vga_ioport_write(s, addr >> s->it_shift, value);
-}
-
-static CPUReadMemoryFunc *vga_mm_read_ctrl[] = {
- &vga_mm_readb,
- &vga_mm_readw,
- &vga_mm_readl,
-};
-
-static CPUWriteMemoryFunc *vga_mm_write_ctrl[] = {
- &vga_mm_writeb,
- &vga_mm_writew,
- &vga_mm_writel,
-};
-
-static void vga_mm_init(VGAState *s, target_phys_addr_t vram_base,
- target_phys_addr_t ctrl_base, int it_shift)
-{
- int s_ioport_ctrl, vga_io_memory;
-
- s->it_shift = it_shift;
- s_ioport_ctrl = cpu_register_io_memory(0, vga_mm_read_ctrl, vga_mm_write_ctrl, s);
- vga_io_memory = cpu_register_io_memory(0, vga_mem_read, vga_mem_write, s);
-
- register_savevm("vga", 0, 2, vga_save, vga_load, s);
-
- cpu_register_physical_memory(ctrl_base, 0x100000, s_ioport_ctrl);
- s->bank_offset = 0;
- cpu_register_physical_memory(vram_base + 0x000a0000, 0x20000, vga_io_memory);
- qemu_register_coalesced_mmio(vram_base + 0x000a0000, 0x20000);
}
--int isa_vga_init(DisplayState *ds, uint8_t *vga_ram_base,
++int isa_vga_init(uint8_t *vga_ram_base,
unsigned long vga_ram_offset, int vga_ram_size)
{
VGAState *s;
s = qemu_mallocz(sizeof(VGAState));
if (!s)
return -1;
+
+ if (vga_ram_size > 16*1024*1024) {
+ fprintf (stderr, "The stdvga/VBE device model has no use for more than 16 Megs of vram. Video ram set to 16M. \n");
+ vga_ram_size = 16*1024*1024;
+ }
-- vga_common_init(s, ds, vga_ram_base, vga_ram_offset, vga_ram_size);
++ vga_common_init(s, vga_ram_base, vga_ram_offset, vga_ram_size);
vga_init(s);
- s->console = graphic_console_init(s->ds, s->update, s->invalidate,
- s->screen_dump, s->text_update, s);
-
-#ifdef CONFIG_BOCHS_VBE
- /* XXX: use optimized standard vga accesses */
- cpu_register_physical_memory(VBE_DISPI_LFB_PHYSICAL_ADDRESS,
- vga_ram_size, vga_ram_offset);
-#endif
- return 0;
-}
-
-int isa_vga_mm_init(DisplayState *ds, uint8_t *vga_ram_base,
- unsigned long vga_ram_offset, int vga_ram_size,
- target_phys_addr_t vram_base, target_phys_addr_t ctrl_base,
- int it_shift)
-{
- VGAState *s;
-
- s = qemu_mallocz(sizeof(VGAState));
- if (!s)
- return -1;
-
- vga_common_init(s, ds, vga_ram_base, vga_ram_offset, vga_ram_size);
- vga_mm_init(s, vram_base, ctrl_base, it_shift);
-
- s->console = graphic_console_init(s->ds, s->update, s->invalidate,
- s->screen_dump, s->text_update, s);
-
#ifdef CONFIG_BOCHS_VBE
/* XXX: use optimized standard vga accesses */
cpu_register_physical_memory(VBE_DISPI_LFB_PHYSICAL_ADDRESS,
return 0;
}
--int pci_vga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base,
++int pci_vga_init(PCIBus *bus, uint8_t *vga_ram_base,
unsigned long vga_ram_offset, int vga_ram_size,
unsigned long vga_bios_offset, int vga_bios_size)
{
return -1;
s = &d->vga_state;
- vga_common_init(s, ds, vga_ram_base, vga_ram_offset, vga_ram_size);
- vga_init(s);
-
- s->console = graphic_console_init(s->ds, s->update, s->invalidate,
- s->screen_dump, s->text_update, s);
+ if (vga_ram_size > 16*1024*1024) {
+ fprintf (stderr, "The stdvga/VBE device model has no use for more than 16 Megs of vram. Video ram set to 16M. \n");
+ vga_ram_size = 16*1024*1024;
+ }
- vga_common_init(s, ds, vga_ram_base, vga_ram_offset, vga_ram_size);
++ vga_common_init(s, vga_ram_base, vga_ram_offset, vga_ram_size);
+ vga_init(s);
s->pci_dev = &d->dev;
pci_conf = d->dev.config;
/********************************************************/
/* vga screen dump */
--static int vga_save_w, vga_save_h;
--
static void vga_save_dpy_update(DisplayState *s,
int x, int y, int w, int h)
{
}
--static void vga_save_dpy_resize(DisplayState *s, int w, int h)
++static void vga_save_dpy_resize(DisplayState *s)
{
-- s->linesize = w * 4;
-- s->data = qemu_mallocz(h * s->linesize);
-- vga_save_w = w;
-- vga_save_h = h;
}
static void vga_save_dpy_refresh(DisplayState *s)
{
}
- static int ppm_save(const char *filename, uint8_t *data,
- int w, int h, int linesize)
-int ppm_save(const char *filename, uint8_t *data,
- int w, int h, int linesize)
++int ppm_save(const char *filename, struct DisplaySurface *ds)
{
FILE *f;
uint8_t *d, *d1;
-- unsigned int v;
++ uint32_t v;
int y, x;
++ uint8_t r, g, b;
f = fopen(filename, "wb");
if (!f)
return -1;
fprintf(f, "P6\n%d %d\n%d\n",
-- w, h, 255);
-- d1 = data;
-- for(y = 0; y < h; y++) {
++ ds->width, ds->height, 255);
++ d1 = ds->data;
++ for(y = 0; y < ds->height; y++) {
d = d1;
-- for(x = 0; x < w; x++) {
-- v = *(uint32_t *)d;
-- fputc((v >> 16) & 0xff, f);
-- fputc((v >> 8) & 0xff, f);
-- fputc((v) & 0xff, f);
-- d += 4;
++ for(x = 0; x < ds->width; x++) {
++ if (ds->pf.bits_per_pixel == 32)
++ v = *(uint32_t *)d;
++ else
++ v = (uint32_t) (*(uint16_t *)d);
++ r = ((v >> ds->pf.rshift) & ds->pf.rmax) * 256 /
++ (ds->pf.rmax + 1);
++ g = ((v >> ds->pf.gshift) & ds->pf.gmax) * 256 /
++ (ds->pf.gmax + 1);
++ b = ((v >> ds->pf.bshift) & ds->pf.bmax) * 256 /
++ (ds->pf.bmax + 1);
++ fputc(r, f);
++ fputc(g, f);
++ fputc(b, f);
++ d += ds->pf.bytes_per_pixel;
}
-- d1 += linesize;
++ d1 += ds->linesize;
}
fclose(f);
return 0;
}
-static void vga_screen_dump_blank(VGAState *s, const char *filename)
-{
- FILE *f;
- unsigned int y, x, w, h;
-
- w = s->last_scr_width * sizeof(uint32_t);
- h = s->last_scr_height;
-
- f = fopen(filename, "wb");
- if (!f)
- return;
- fprintf(f, "P6\n%d %d\n%d\n", w, h, 255);
- for (y = 0; y < h; y++) {
- for (x = 0; x < w; x++) {
- fputc(0, f);
- }
- }
- fclose(f);
-}
-
-static void vga_screen_dump_common(VGAState *s, const char *filename,
- int w, int h)
+/* save the vga display in a PPM image even if no display is
+ available */
+static void vga_screen_dump(void *opaque, const char *filename)
{
+ VGAState *s = (VGAState *)opaque;
DisplayState *saved_ds, ds1, *ds = &ds1;
++ DisplayChangeListener dcl;
++ int w, h;
++
++ s->get_resolution(s, &w, &h);
/* XXX: this is a little hackish */
vga_invalidate_display(s);
saved_ds = s->ds;
memset(ds, 0, sizeof(DisplayState));
-- ds->dpy_update = vga_save_dpy_update;
-- ds->dpy_resize = vga_save_dpy_resize;
-- ds->dpy_refresh = vga_save_dpy_refresh;
-- ds->depth = 32;
--
- ds->linesize = w * sizeof(uint32_t);
- ds->data = qemu_mallocz(h * ds->linesize);
++ memset(&dcl, 0, sizeof(DisplayChangeListener));
++ dcl.dpy_update = vga_save_dpy_update;
++ dcl.dpy_resize = vga_save_dpy_resize;
++ dcl.dpy_refresh = vga_save_dpy_refresh;
++ register_displaychangelistener(ds, &dcl);
++ ds->surface = qemu_create_displaysurface(ds, w, h, 32, 4 * w);
++
s->ds = ds;
s->graphic_mode = -1;
vga_update_display(s);
- ppm_save(filename, ds->data, w, h, ds->linesize);
- qemu_free(ds->data);
- s->ds = saved_ds;
-}
-
-static void vga_screen_dump_graphic(VGAState *s, const char *filename)
-{
- int w, h;
-
- s->get_resolution(s, &w, &h);
- vga_screen_dump_common(s, filename, w, h);
-}
-
-static void vga_screen_dump_text(VGAState *s, const char *filename)
-{
- int w, h, cwidth, cheight;
- if (ds->data) {
- ppm_save(filename, ds->data, vga_save_w, vga_save_h,
- s->ds->linesize);
- qemu_free(ds->data);
- }
- vga_get_text_resolution(s, &w, &h, &cwidth, &cheight);
- vga_screen_dump_common(s, filename, w * cwidth, h * cheight);
-}
-
-/* save the vga display in a PPM image even if no display is
- available */
-static void vga_screen_dump(void *opaque, const char *filename)
-{
- VGAState *s = (VGAState *)opaque;
++ ppm_save(filename, ds->surface);
+
- if (!(s->ar_index & 0x20))
- vga_screen_dump_blank(s, filename);
- else if (s->gr[6] & 1)
- vga_screen_dump_graphic(s, filename);
- else
- vga_screen_dump_text(s, filename);
++ qemu_free_displaysurface(ds);
+ s->ds = saved_ds;
}
VGA_STATE_COMMON_BOCHS_VBE \
/* display refresh support */ \
DisplayState *ds; \
-- QEMUConsole *console; \
uint32_t font_offsets[2]; \
int graphic_mode; \
uint8_t shift_control; \
return (v << 2) | (b << 1) | b;
}
--void vga_common_init(VGAState *s, DisplayState *ds, uint8_t *vga_ram_base,
- ram_addr_t vga_ram_offset, int vga_ram_size);
-void vga_init(VGAState *s);
-void vga_reset(void *s);
-
-void vga_dirty_log_start(VGAState *s);
-void vga_dirty_log_stop(VGAState *s);
-
++void vga_common_init(VGAState *s, uint8_t *vga_ram_base,
+ unsigned long vga_ram_offset, int vga_ram_size);
uint32_t vga_mem_readb(void *opaque, target_phys_addr_t addr);
void vga_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val);
void vga_invalidate_scanlines(VGAState *s, int y1, int y2);
-int ppm_save(const char *filename, uint8_t *data,
- int w, int h, int linesize);
++int ppm_save(const char *filename, struct DisplaySurface *ds);
void vga_draw_cursor_line_8(uint8_t *d1, const uint8_t *src1,
int poffset, int w,
if (data)
qemu_free(data);
close(fd);
- return -1;
+ return ret;
}
+
+#endif /*CONFIG_DM*/
return -1;
}
--#ifndef _WIN32
++#ifndef NO_UNIX_SOCKETS
int unix_socket_incoming(const char *path)
{
int s;
--- /dev/null
-static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
+ /*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 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 "qemu-common.h"
+ #include "net.h"
+ #include "console.h"
+ #include "sysemu.h"
+ #include "qemu-timer.h"
+ #include "qemu-char.h"
+ #include "audio/audio.h"
+
+ #include <unistd.h>
+ #include <fcntl.h>
+ #include <signal.h>
+ #include <time.h>
+ #include <errno.h>
+ #include <sys/time.h>
+ #include <zlib.h>
+
+ #ifndef _WIN32
+ #include <sys/times.h>
+ #include <sys/wait.h>
+ #include <termios.h>
+ #include <sys/mman.h>
+ #include <sys/ioctl.h>
+ #include <sys/resource.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <net/if.h>
+ #ifdef __NetBSD__
+ #include <net/if_tap.h>
+ #endif
+ #ifdef __linux__
+ #include <linux/if_tun.h>
+ #endif
+ #include <arpa/inet.h>
+ #include <dirent.h>
+ #include <netdb.h>
+ #include <sys/select.h>
+ #ifdef _BSD
+ #include <sys/stat.h>
+ #ifdef __FreeBSD__
+ #include <libutil.h>
+ #else
+ #include <util.h>
+ #endif
+ #elif defined (__GLIBC__) && defined (__FreeBSD_kernel__)
+ #include <freebsd/stdlib.h>
+ #else
+ #ifdef __linux__
+ #include <pty.h>
+ #include <malloc.h>
+ #include <linux/rtc.h>
+
+ /* For the benefit of older linux systems which don't supply it,
+ we use a local copy of hpet.h. */
+ /* #include <linux/hpet.h> */
+ #include "hpet.h"
+
+ #include <linux/ppdev.h>
+ #include <linux/parport.h>
+ #endif
+ #ifdef __sun__
+ #include <sys/stat.h>
+ #include <sys/ethernet.h>
+ #include <sys/sockio.h>
+ #include <netinet/arp.h>
+ #include <netinet/in.h>
+ #include <netinet/in_systm.h>
+ #include <netinet/ip.h>
+ #include <netinet/ip_icmp.h> // must come after ip.h
+ #include <netinet/udp.h>
+ #include <netinet/tcp.h>
+ #include <net/if.h>
+ #include <syslog.h>
+ #include <stropts.h>
+ #endif
+ #endif
+ #endif
+
+ #include "qemu_socket.h"
+
+ #if defined(CONFIG_SLIRP)
+ #include "libslirp.h"
+ #endif
+
+ #if defined(__OpenBSD__)
+ #include <util.h>
+ #endif
+
+ #if defined(CONFIG_VDE)
+ #include <libvdeplug.h>
+ #endif
+
+ #ifdef _WIN32
+ #include <malloc.h>
+ #include <sys/timeb.h>
+ #include <mmsystem.h>
+ #define getopt_long_only getopt_long
+ #define memalign(align, size) malloc(size)
+ #endif
+
+ static VLANState *first_vlan;
+
++static TAPState *head_net_tap;
++
++void net_tap_shutdown_all(void)
++{
++ struct IOHandlerRecord **pioh, *ioh;
++
++ while (head_net_tap) {
++ qemu_set_fd_handler2(head_net_tap->fd, 0,0,0,0);
++ close(head_net_tap->fd);
++ head_net_tap = head_net_tap->next;
++ }
++}
++
+ /***********************************************************/
+ /* network device redirectors */
+
+ #if defined(DEBUG_NET) || defined(DEBUG_SLIRP)
+ static void hex_dump(FILE *f, const uint8_t *buf, int size)
+ {
+ int len, i, j, c;
+
+ for(i=0;i<size;i+=16) {
+ len = size - i;
+ if (len > 16)
+ len = 16;
+ fprintf(f, "%08x ", i);
+ for(j=0;j<16;j++) {
+ if (j < len)
+ fprintf(f, " %02x", buf[i+j]);
+ else
+ fprintf(f, " ");
+ }
+ fprintf(f, " ");
+ for(j=0;j<len;j++) {
+ c = buf[i+j];
+ if (c < ' ' || c > '~')
+ c = '.';
+ fprintf(f, "%c", c);
+ }
+ fprintf(f, "\n");
+ }
+ }
+ #endif
+
+ static int parse_macaddr(uint8_t *macaddr, const char *p)
+ {
+ int i;
+ char *last_char;
+ long int offset;
+
+ errno = 0;
+ offset = strtol(p, &last_char, 0);
+ if (0 == errno && '\0' == *last_char &&
+ offset >= 0 && offset <= 0xFFFFFF) {
+ macaddr[3] = (offset & 0xFF0000) >> 16;
+ macaddr[4] = (offset & 0xFF00) >> 8;
+ macaddr[5] = offset & 0xFF;
+ return 0;
+ } else {
+ for(i = 0; i < 6; i++) {
+ macaddr[i] = strtol(p, (char **)&p, 16);
+ if (i == 5) {
+ if (*p != '\0')
+ return -1;
+ } else {
+ if (*p != ':' && *p != '-')
+ return -1;
+ p++;
+ }
+ }
+ return 0;
+ }
+
+ return -1;
+ }
+
-#else
++static int get_str_sep(char *buf, size_t buf_size, const char **pp, int sep)
+ {
+ const char *p, *p1;
+ int len;
+ p = *pp;
+ p1 = strchr(p, sep);
+ if (!p1)
+ return -1;
+ len = p1 - p;
+ p1++;
+ if (buf_size > 0) {
+ if (len > buf_size - 1)
+ len = buf_size - 1;
+ memcpy(buf, p, len);
+ buf[len] = '\0';
+ }
+ *pp = p1;
+ return 0;
+ }
+
+ int parse_host_src_port(struct sockaddr_in *haddr,
+ struct sockaddr_in *saddr,
+ const char *input_str)
+ {
+ char *str = strdup(input_str);
+ char *host_str = str;
+ char *src_str;
+ const char *src_str2;
+ char *ptr;
+
+ /*
+ * Chop off any extra arguments at the end of the string which
+ * would start with a comma, then fill in the src port information
+ * if it was provided else use the "any address" and "any port".
+ */
+ if ((ptr = strchr(str,',')))
+ *ptr = '\0';
+
+ if ((src_str = strchr(input_str,'@'))) {
+ *src_str = '\0';
+ src_str++;
+ }
+
+ if (parse_host_port(haddr, host_str) < 0)
+ goto fail;
+
+ src_str2 = src_str;
+ if (!src_str || *src_str == '\0')
+ src_str2 = ":0";
+
+ if (parse_host_port(saddr, src_str2) < 0)
+ goto fail;
+
+ free(str);
+ return(0);
+
+ fail:
+ free(str);
+ return -1;
+ }
+
+ int parse_host_port(struct sockaddr_in *saddr, const char *str)
+ {
+ char buf[512];
+ struct hostent *he;
+ const char *p, *r;
+ int port;
+
+ p = str;
+ if (get_str_sep(buf, sizeof(buf), &p, ':') < 0)
+ return -1;
+ saddr->sin_family = AF_INET;
+ if (buf[0] == '\0') {
+ saddr->sin_addr.s_addr = 0;
+ } else {
+ if (qemu_isdigit(buf[0])) {
+ if (!inet_aton(buf, &saddr->sin_addr))
+ return -1;
+ } else {
+ if ((he = gethostbyname(buf)) == NULL)
+ return - 1;
+ saddr->sin_addr = *(struct in_addr *)he->h_addr;
+ }
+ }
+ port = strtol(p, (char **)&r, 0);
+ if (r == p)
+ return -1;
+ saddr->sin_port = htons(port);
+ return 0;
+ }
+
+ #if !defined(_WIN32) && 0
+ static int parse_unix_path(struct sockaddr_un *uaddr, const char *str)
+ {
+ const char *p;
+ int len;
+
+ len = MIN(108, strlen(str));
+ p = strchr(str, ',');
+ if (p)
+ len = MIN(len, p - str);
+
+ memset(uaddr, 0, sizeof(*uaddr));
+
+ uaddr->sun_family = AF_UNIX;
+ memcpy(uaddr->sun_path, str, len);
+
+ return 0;
+ }
+ #endif
+
+ void qemu_format_nic_info_str(VLANClientState *vc, uint8_t macaddr[6])
+ {
+ snprintf(vc->info_str, sizeof(vc->info_str),
+ "model=%s,macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
+ vc->model,
+ macaddr[0], macaddr[1], macaddr[2],
+ macaddr[3], macaddr[4], macaddr[5]);
+ }
+
+ static char *assign_name(VLANClientState *vc1, const char *model)
+ {
+ VLANState *vlan;
+ char buf[256];
+ int id = 0;
+
+ for (vlan = first_vlan; vlan; vlan = vlan->next) {
+ VLANClientState *vc;
+
+ for (vc = vlan->first_client; vc; vc = vc->next)
+ if (vc != vc1 && strcmp(vc->model, model) == 0)
+ id++;
+ }
+
+ snprintf(buf, sizeof(buf), "%s.%d", model, id);
+
+ return strdup(buf);
+ }
+
+ VLANClientState *qemu_new_vlan_client(VLANState *vlan,
+ const char *model,
+ const char *name,
+ IOReadHandler *fd_read,
+ IOCanRWHandler *fd_can_read,
+ void *opaque)
+ {
+ VLANClientState *vc, **pvc;
+ vc = qemu_mallocz(sizeof(VLANClientState));
+ if (!vc)
+ return NULL;
+ vc->model = strdup(model);
+ if (name)
+ vc->name = strdup(name);
+ else
+ vc->name = assign_name(vc, model);
+ vc->fd_read = fd_read;
+ vc->fd_can_read = fd_can_read;
+ vc->opaque = opaque;
+ vc->vlan = vlan;
+
+ vc->next = NULL;
+ pvc = &vlan->first_client;
+ while (*pvc != NULL)
+ pvc = &(*pvc)->next;
+ *pvc = vc;
+ return vc;
+ }
+
+ void qemu_del_vlan_client(VLANClientState *vc)
+ {
+ VLANClientState **pvc = &vc->vlan->first_client;
+
+ while (*pvc != NULL)
+ if (*pvc == vc) {
+ *pvc = vc->next;
+ free(vc->name);
+ free(vc->model);
+ free(vc);
+ break;
+ } else
+ pvc = &(*pvc)->next;
+ }
+
+ int qemu_can_send_packet(VLANClientState *vc1)
+ {
+ VLANState *vlan = vc1->vlan;
+ VLANClientState *vc;
+
+ for(vc = vlan->first_client; vc != NULL; vc = vc->next) {
+ if (vc != vc1) {
+ if (vc->fd_can_read && vc->fd_can_read(vc->opaque))
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ void qemu_send_packet(VLANClientState *vc1, const uint8_t *buf, int size)
+ {
+ VLANState *vlan = vc1->vlan;
+ VLANClientState *vc;
+
+ if (vc1->link_down)
+ return;
+
+ #ifdef DEBUG_NET
+ printf("vlan %d send:\n", vlan->id);
+ hex_dump(stdout, buf, size);
+ #endif
+ for(vc = vlan->first_client; vc != NULL; vc = vc->next) {
+ if (vc != vc1 && !vc->link_down) {
+ vc->fd_read(vc->opaque, buf, size);
+ }
+ }
+ }
+
+ static ssize_t vc_sendv_compat(VLANClientState *vc, const struct iovec *iov,
+ int iovcnt)
+ {
+ uint8_t buffer[4096];
+ size_t offset = 0;
+ int i;
+
+ for (i = 0; i < iovcnt; i++) {
+ size_t len;
+
+ len = MIN(sizeof(buffer) - offset, iov[i].iov_len);
+ memcpy(buffer + offset, iov[i].iov_base, len);
+ offset += len;
+ }
+
+ vc->fd_read(vc->opaque, buffer, offset);
+
+ return offset;
+ }
+
+ ssize_t qemu_sendv_packet(VLANClientState *vc1, const struct iovec *iov,
+ int iovcnt)
+ {
+ VLANState *vlan = vc1->vlan;
+ VLANClientState *vc;
+ ssize_t max_len = 0;
+
+ for (vc = vlan->first_client; vc != NULL; vc = vc->next) {
+ ssize_t len = 0;
+
+ if (vc == vc1)
+ continue;
+
+ if (vc->fd_readv)
+ len = vc->fd_readv(vc->opaque, iov, iovcnt);
+ else if (vc->fd_read)
+ len = vc_sendv_compat(vc, iov, iovcnt);
+
+ max_len = MAX(max_len, len);
+ }
+
+ return max_len;
+ }
+
+ #if defined(CONFIG_SLIRP)
+
+ /* slirp network adapter */
+
+ static int slirp_inited;
+ static int slirp_restrict;
+ static char *slirp_ip;
+ static VLANClientState *slirp_vc;
+
+ int slirp_can_output(void)
+ {
+ return !slirp_vc || qemu_can_send_packet(slirp_vc);
+ }
+
+ void slirp_output(const uint8_t *pkt, int pkt_len)
+ {
+ #ifdef DEBUG_SLIRP
+ printf("slirp output:\n");
+ hex_dump(stdout, pkt, pkt_len);
+ #endif
+ if (!slirp_vc)
+ return;
+ qemu_send_packet(slirp_vc, pkt, pkt_len);
+ }
+
+ int slirp_is_inited(void)
+ {
+ return slirp_inited;
+ }
+
+ static void slirp_receive(void *opaque, const uint8_t *buf, int size)
+ {
+ #ifdef DEBUG_SLIRP
+ printf("slirp input:\n");
+ hex_dump(stdout, buf, size);
+ #endif
+ slirp_input(buf, size);
+ }
+
+ static int net_slirp_init(VLANState *vlan, const char *model, const char *name)
+ {
+ if (!slirp_inited) {
+ slirp_inited = 1;
+ slirp_init(slirp_restrict, slirp_ip);
+ }
+ slirp_vc = qemu_new_vlan_client(vlan, model, name,
+ slirp_receive, NULL, NULL);
+ slirp_vc->info_str[0] = '\0';
+ return 0;
+ }
+
+ void net_slirp_redir(const char *redir_str)
+ {
+ int is_udp;
+ char buf[256], *r;
+ const char *p;
+ struct in_addr guest_addr;
+ int host_port, guest_port;
+
+ if (!slirp_inited) {
+ slirp_inited = 1;
+ slirp_init(slirp_restrict, slirp_ip);
+ }
+
+ p = redir_str;
+ if (get_str_sep(buf, sizeof(buf), &p, ':') < 0)
+ goto fail;
+ if (!strcmp(buf, "tcp")) {
+ is_udp = 0;
+ } else if (!strcmp(buf, "udp")) {
+ is_udp = 1;
+ } else {
+ goto fail;
+ }
+
+ if (get_str_sep(buf, sizeof(buf), &p, ':') < 0)
+ goto fail;
+ host_port = strtol(buf, &r, 0);
+ if (r == buf)
+ goto fail;
+
+ if (get_str_sep(buf, sizeof(buf), &p, ':') < 0)
+ goto fail;
+ if (buf[0] == '\0') {
+ pstrcpy(buf, sizeof(buf), "10.0.2.15");
+ }
+ if (!inet_aton(buf, &guest_addr))
+ goto fail;
+
+ guest_port = strtol(p, &r, 0);
+ if (r == p)
+ goto fail;
+
+ if (slirp_redir(is_udp, host_port, guest_addr, guest_port) < 0) {
+ fprintf(stderr, "qemu: could not set up redirection\n");
+ exit(1);
+ }
+ return;
+ fail:
+ fprintf(stderr, "qemu: syntax: -redir [tcp|udp]:host-port:[guest-host]:guest-port\n");
+ exit(1);
+ }
+
+ #ifndef _WIN32
+
+ static char smb_dir[1024];
+
+ static void erase_dir(char *dir_name)
+ {
+ DIR *d;
+ struct dirent *de;
+ char filename[1024];
+
+ /* erase all the files in the directory */
+ if ((d = opendir(dir_name)) != 0) {
+ for(;;) {
+ de = readdir(d);
+ if (!de)
+ break;
+ if (strcmp(de->d_name, ".") != 0 &&
+ strcmp(de->d_name, "..") != 0) {
+ snprintf(filename, sizeof(filename), "%s/%s",
+ smb_dir, de->d_name);
+ if (unlink(filename) != 0) /* is it a directory? */
+ erase_dir(filename);
+ }
+ }
+ closedir(d);
+ rmdir(dir_name);
+ }
+ }
+
+ /* automatic user mode samba server configuration */
+ static void smb_exit(void)
+ {
+ erase_dir(smb_dir);
+ }
+
+ /* automatic user mode samba server configuration */
+ void net_slirp_smb(const char *exported_dir)
+ {
+ char smb_conf[1024];
+ char smb_cmdline[1024];
+ FILE *f;
+
+ if (!slirp_inited) {
+ slirp_inited = 1;
+ slirp_init(slirp_restrict, slirp_ip);
+ }
+
+ /* XXX: better tmp dir construction */
+ snprintf(smb_dir, sizeof(smb_dir), "/tmp/qemu-smb.%d", getpid());
+ if (mkdir(smb_dir, 0700) < 0) {
+ fprintf(stderr, "qemu: could not create samba server dir '%s'\n", smb_dir);
+ exit(1);
+ }
+ snprintf(smb_conf, sizeof(smb_conf), "%s/%s", smb_dir, "smb.conf");
+
+ f = fopen(smb_conf, "w");
+ if (!f) {
+ fprintf(stderr, "qemu: could not create samba server configuration file '%s'\n", smb_conf);
+ exit(1);
+ }
+ fprintf(f,
+ "[global]\n"
+ "private dir=%s\n"
+ "smb ports=0\n"
+ "socket address=127.0.0.1\n"
+ "pid directory=%s\n"
+ "lock directory=%s\n"
+ "log file=%s/log.smbd\n"
+ "smb passwd file=%s/smbpasswd\n"
+ "security = share\n"
+ "[qemu]\n"
+ "path=%s\n"
+ "read only=no\n"
+ "guest ok=yes\n",
+ smb_dir,
+ smb_dir,
+ smb_dir,
+ smb_dir,
+ smb_dir,
+ exported_dir
+ );
+ fclose(f);
+ atexit(smb_exit);
+
+ snprintf(smb_cmdline, sizeof(smb_cmdline), "%s -s %s",
+ SMBD_COMMAND, smb_conf);
+
+ slirp_add_exec(0, smb_cmdline, 4, 139);
+ }
+
+ #endif /* !defined(_WIN32) */
+ void do_info_slirp(void)
+ {
+ slirp_stats();
+ }
+
+ #endif /* CONFIG_SLIRP */
+
+ #if !defined(_WIN32)
+
+ typedef struct TAPState {
+ VLANClientState *vc;
+ int fd;
++ struct TAPState *next;
+ char down_script[1024];
+ char down_script_arg[128];
++ char script_arg[1024];
+ } TAPState;
+
+ #ifdef HAVE_IOVEC
+ static ssize_t tap_receive_iov(void *opaque, const struct iovec *iov,
+ int iovcnt)
+ {
+ TAPState *s = opaque;
+ ssize_t len;
+
+ do {
+ len = writev(s->fd, iov, iovcnt);
+ } while (len == -1 && (errno == EINTR || errno == EAGAIN));
+
+ return len;
+ }
+ #endif
+
+ static void tap_receive(void *opaque, const uint8_t *buf, int size)
+ {
+ TAPState *s = opaque;
+ int ret;
+ for(;;) {
+ ret = write(s->fd, buf, size);
+ if (ret < 0 && (errno == EINTR || errno == EAGAIN)) {
+ } else {
+ break;
+ }
+ }
+ }
+
+ static void tap_send(void *opaque)
+ {
+ TAPState *s = opaque;
+ uint8_t buf[4096];
+ int size;
+
+ #ifdef __sun__
+ struct strbuf sbuf;
+ int f = 0;
+ sbuf.maxlen = sizeof(buf);
+ sbuf.buf = buf;
+ size = getmsg(s->fd, NULL, &sbuf, &f) >=0 ? sbuf.len : -1;
+ #else
+ size = read(s->fd, buf, sizeof(buf));
+ #endif
+ if (size > 0) {
+ qemu_send_packet(s->vc, buf, size);
+ }
+ }
+
+ /* fd support */
+
+ static TAPState *net_tap_fd_init(VLANState *vlan,
+ const char *model,
+ const char *name,
+ int fd)
+ {
+ TAPState *s;
+
+ s = qemu_mallocz(sizeof(TAPState));
+ if (!s)
+ return NULL;
+ s->fd = fd;
+ s->vc = qemu_new_vlan_client(vlan, model, name, tap_receive, NULL, s);
++ s->next = head_net_tap;
++ head_net_tap = s;
+ #ifdef HAVE_IOVEC
+ s->vc->fd_readv = tap_receive_iov;
+ #endif
+ qemu_set_fd_handler(s->fd, tap_send, NULL, s);
+ snprintf(s->vc->info_str, sizeof(s->vc->info_str), "fd=%d", fd);
+ return s;
+ }
+
+ #if defined (_BSD) || defined (__FreeBSD_kernel__)
+ static int tap_open(char *ifname, int ifname_size)
+ {
+ int fd;
++#ifndef TAPGIFNAME
+ char *dev;
+ struct stat s;
++#endif
+
+ TFR(fd = open("/dev/tap", O_RDWR));
+ if (fd < 0) {
+ fprintf(stderr, "warning: could not open /dev/tap: no virtual network emulation\n");
+ return -1;
+ }
+
++#ifdef TAPGIFNAME
++ if (ioctl (fd, TAPGIFNAME, (void*)&ifr) < 0) {
++ fprintf(stderr, "warning: could not open get tap name: %s\n",
++ strerror(errno));
++ return -1;
++ }
++ pstrcpy(ifname, ifname_size, ifr.ifr_name);
++#else
+ fstat(fd, &s);
+ dev = devname(s.st_rdev, S_IFCHR);
+ pstrcpy(ifname, ifname_size, dev);
++#endif
+
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+ return fd;
+ }
+ #elif defined(__sun__)
+ #define TUNNEWPPA (('T'<<16) | 0x0001)
+ /*
+ * Allocate TAP device, returns opened fd.
+ * Stores dev name in the first arg(must be large enough).
+ */
+ int tap_alloc(char *dev, size_t dev_size)
+ {
+ int tap_fd, if_fd, ppa = -1;
+ static int ip_fd = 0;
+ char *ptr;
+
+ static int arp_fd = 0;
+ int ip_muxid, arp_muxid;
+ struct strioctl strioc_if, strioc_ppa;
+ int link_type = I_PLINK;;
+ struct lifreq ifr;
+ char actual_name[32] = "";
+
+ memset(&ifr, 0x0, sizeof(ifr));
+
+ if( *dev ){
+ ptr = dev;
+ while( *ptr && !qemu_isdigit((int)*ptr) ) ptr++;
+ ppa = atoi(ptr);
+ }
+
+ /* Check if IP device was opened */
+ if( ip_fd )
+ close(ip_fd);
+
+ TFR(ip_fd = open("/dev/udp", O_RDWR, 0));
+ if (ip_fd < 0) {
+ syslog(LOG_ERR, "Can't open /dev/ip (actually /dev/udp)");
+ return -1;
+ }
+
+ TFR(tap_fd = open("/dev/tap", O_RDWR, 0));
+ if (tap_fd < 0) {
+ syslog(LOG_ERR, "Can't open /dev/tap");
+ return -1;
+ }
+
+ /* Assign a new PPA and get its unit number. */
+ strioc_ppa.ic_cmd = TUNNEWPPA;
+ strioc_ppa.ic_timout = 0;
+ strioc_ppa.ic_len = sizeof(ppa);
+ strioc_ppa.ic_dp = (char *)&ppa;
+ if ((ppa = ioctl (tap_fd, I_STR, &strioc_ppa)) < 0)
+ syslog (LOG_ERR, "Can't assign new interface");
+
+ TFR(if_fd = open("/dev/tap", O_RDWR, 0));
+ if (if_fd < 0) {
+ syslog(LOG_ERR, "Can't open /dev/tap (2)");
+ return -1;
+ }
+ if(ioctl(if_fd, I_PUSH, "ip") < 0){
+ syslog(LOG_ERR, "Can't push IP module");
+ return -1;
+ }
+
+ if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) < 0)
+ syslog(LOG_ERR, "Can't get flags\n");
+
+ snprintf (actual_name, 32, "tap%d", ppa);
+ pstrcpy(ifr.lifr_name, sizeof(ifr.lifr_name), actual_name);
+
+ ifr.lifr_ppa = ppa;
+ /* Assign ppa according to the unit number returned by tun device */
+
+ if (ioctl (if_fd, SIOCSLIFNAME, &ifr) < 0)
+ syslog (LOG_ERR, "Can't set PPA %d", ppa);
+ if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) <0)
+ syslog (LOG_ERR, "Can't get flags\n");
+ /* Push arp module to if_fd */
+ if (ioctl (if_fd, I_PUSH, "arp") < 0)
+ syslog (LOG_ERR, "Can't push ARP module (2)");
+
+ /* Push arp module to ip_fd */
+ if (ioctl (ip_fd, I_POP, NULL) < 0)
+ syslog (LOG_ERR, "I_POP failed\n");
+ if (ioctl (ip_fd, I_PUSH, "arp") < 0)
+ syslog (LOG_ERR, "Can't push ARP module (3)\n");
+ /* Open arp_fd */
+ TFR(arp_fd = open ("/dev/tap", O_RDWR, 0));
+ if (arp_fd < 0)
+ syslog (LOG_ERR, "Can't open %s\n", "/dev/tap");
+
+ /* Set ifname to arp */
+ strioc_if.ic_cmd = SIOCSLIFNAME;
+ strioc_if.ic_timout = 0;
+ strioc_if.ic_len = sizeof(ifr);
+ strioc_if.ic_dp = (char *)𝔦
+ if (ioctl(arp_fd, I_STR, &strioc_if) < 0){
+ syslog (LOG_ERR, "Can't set ifname to arp\n");
+ }
+
+ if((ip_muxid = ioctl(ip_fd, I_LINK, if_fd)) < 0){
+ syslog(LOG_ERR, "Can't link TAP device to IP");
+ return -1;
+ }
+
+ if ((arp_muxid = ioctl (ip_fd, link_type, arp_fd)) < 0)
+ syslog (LOG_ERR, "Can't link TAP device to ARP");
+
+ close (if_fd);
+
+ memset(&ifr, 0x0, sizeof(ifr));
+ pstrcpy(ifr.lifr_name, sizeof(ifr.lifr_name), actual_name);
+ ifr.lifr_ip_muxid = ip_muxid;
+ ifr.lifr_arp_muxid = arp_muxid;
+
+ if (ioctl (ip_fd, SIOCSLIFMUXID, &ifr) < 0)
+ {
+ ioctl (ip_fd, I_PUNLINK , arp_muxid);
+ ioctl (ip_fd, I_PUNLINK, ip_muxid);
+ syslog (LOG_ERR, "Can't set multiplexor id");
+ }
+
+ snprintf(dev, dev_size, "tap%d", ppa);
+ return tap_fd;
+ }
+
+ static int tap_open(char *ifname, int ifname_size)
+ {
+ char dev[10]="";
+ int fd;
+ if( (fd = tap_alloc(dev, sizeof(dev))) < 0 ){
+ fprintf(stderr, "Cannot allocate TAP device\n");
+ return -1;
+ }
+ pstrcpy(ifname, ifname_size, dev);
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+ return fd;
+ }
+ #elif defined (_AIX)
+ static int tap_open(char *ifname, int ifname_size)
+ {
+ fprintf (stderr, "no tap on AIX\n");
+ return -1;
+ }
-static int launch_script(const char *setup_script, const char *ifname, int fd)
++#elif defined(__linux__)
+ static int tap_open(char *ifname, int ifname_size)
+ {
+ struct ifreq ifr;
+ int fd, ret;
+
+ TFR(fd = open("/dev/net/tun", O_RDWR));
+ if (fd < 0) {
+ fprintf(stderr, "warning: could not open /dev/net/tun: no virtual network emulation\n");
+ return -1;
+ }
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+ if (ifname[0] != '\0')
+ pstrcpy(ifr.ifr_name, IFNAMSIZ, ifname);
+ else
+ pstrcpy(ifr.ifr_name, IFNAMSIZ, "tap%d");
+ ret = ioctl(fd, TUNSETIFF, (void *) &ifr);
+ if (ret != 0) {
+ fprintf(stderr, "warning: could not configure /dev/net/tun: no virtual network emulation\n");
+ close(fd);
+ return -1;
+ }
+ pstrcpy(ifname, ifname_size, ifr.ifr_name);
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+ return fd;
+ }
+ #endif
+
- char *args[3];
++static int launch_script(const char *setup_script, const char *ifname,
++ const char *script_arg, int fd)
+ {
+ int pid, status;
- const char *setup_script, const char *down_script)
++ char *args[4];
+ char **parg;
+
+ /* try to launch network script */
+ pid = fork();
+ if (pid >= 0) {
+ if (pid == 0) {
+ int open_max = sysconf (_SC_OPEN_MAX), i;
+ for (i = 0; i < open_max; i++)
+ if (i != STDIN_FILENO &&
+ i != STDOUT_FILENO &&
+ i != STDERR_FILENO &&
+ i != fd)
+ close(i);
+
+ parg = args;
+ *parg++ = (char *)setup_script;
+ *parg++ = (char *)ifname;
++ if (script_arg && script_arg[0])
++ *parg++ = (char *)script_arg;
+ *parg++ = NULL;
+ execv(setup_script, args);
+ _exit(1);
+ }
+ while (waitpid(pid, &status, 0) != pid);
+ if (!WIFEXITED(status) ||
+ WEXITSTATUS(status) != 0) {
+ fprintf(stderr, "%s: could not launch network script\n",
+ setup_script);
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ static int net_tap_init(VLANState *vlan, const char *model,
+ const char *name, const char *ifname1,
- if (launch_script(setup_script, ifname, fd))
++ const char *setup_script, const char *down_script,
++ const char *script_arg)
+ {
+ TAPState *s;
+ int fd;
+ char ifname[128];
+
+ if (ifname1 != NULL)
+ pstrcpy(ifname, sizeof(ifname), ifname1);
+ else
+ ifname[0] = '\0';
+ TFR(fd = tap_open(ifname, sizeof(ifname)));
+ if (fd < 0)
+ return -1;
+
+ if (!setup_script || !strcmp(setup_script, "no"))
+ setup_script = "";
+ if (setup_script[0] != '\0') {
- char setup_script[1024], down_script[1024];
++ if (launch_script(setup_script, ifname, script_arg, fd))
+ return -1;
+ }
+ s = net_tap_fd_init(vlan, model, name, fd);
+ if (!s)
+ return -1;
+ snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+ "ifname=%s,script=%s,downscript=%s",
+ ifname, setup_script, down_script);
+ if (down_script && strcmp(down_script, "no")) {
+ snprintf(s->down_script, sizeof(s->down_script), "%s", down_script);
+ snprintf(s->down_script_arg, sizeof(s->down_script_arg), "%s", ifname);
+ }
++ if (script_arg && script_arg[0])
++ snprintf(s->script_arg, sizeof(s->script_arg), "%s", script_arg);
++ else
++ s->script_arg[0] = '\0';
+ return 0;
+ }
+
+ #endif /* !_WIN32 */
+
+ #if defined(CONFIG_VDE)
+ typedef struct VDEState {
+ VLANClientState *vc;
+ VDECONN *vde;
+ } VDEState;
+
+ static void vde_to_qemu(void *opaque)
+ {
+ VDEState *s = opaque;
+ uint8_t buf[4096];
+ int size;
+
+ size = vde_recv(s->vde, buf, sizeof(buf), 0);
+ if (size > 0) {
+ qemu_send_packet(s->vc, buf, size);
+ }
+ }
+
+ static void vde_from_qemu(void *opaque, const uint8_t *buf, int size)
+ {
+ VDEState *s = opaque;
+ int ret;
+ for(;;) {
+ ret = vde_send(s->vde, buf, size, 0);
+ if (ret < 0 && errno == EINTR) {
+ } else {
+ break;
+ }
+ }
+ }
+
+ static int net_vde_init(VLANState *vlan, const char *model,
+ const char *name, const char *sock,
+ int port, const char *group, int mode)
+ {
+ VDEState *s;
+ char *init_group = strlen(group) ? (char *)group : NULL;
+ char *init_sock = strlen(sock) ? (char *)sock : NULL;
+
+ struct vde_open_args args = {
+ .port = port,
+ .group = init_group,
+ .mode = mode,
+ };
+
+ s = qemu_mallocz(sizeof(VDEState));
+ if (!s)
+ return -1;
+ s->vde = vde_open(init_sock, "QEMU", &args);
+ if (!s->vde){
+ free(s);
+ return -1;
+ }
+ s->vc = qemu_new_vlan_client(vlan, model, name, vde_from_qemu, NULL, s);
+ qemu_set_fd_handler(vde_datafd(s->vde), vde_to_qemu, NULL, s);
+ snprintf(s->vc->info_str, sizeof(s->vc->info_str), "sock=%s,fd=%d",
+ sock, vde_datafd(s->vde));
+ return 0;
+ }
+ #endif
+
+ /* network connection */
+ typedef struct NetSocketState {
+ VLANClientState *vc;
+ int fd;
+ int state; /* 0 = getting length, 1 = getting data */
+ int index;
+ int packet_len;
+ uint8_t buf[4096];
+ struct sockaddr_in dgram_dst; /* contains inet host and port destination iff connectionless (SOCK_DGRAM) */
+ } NetSocketState;
+
+ typedef struct NetSocketListenState {
+ VLANState *vlan;
+ char *model;
+ char *name;
+ int fd;
+ } NetSocketListenState;
+
+ /* XXX: we consider we can send the whole packet without blocking */
+ static void net_socket_receive(void *opaque, const uint8_t *buf, int size)
+ {
+ NetSocketState *s = opaque;
+ uint32_t len;
+ len = htonl(size);
+
+ send_all(s->fd, (const uint8_t *)&len, sizeof(len));
+ send_all(s->fd, buf, size);
+ }
+
+ static void net_socket_receive_dgram(void *opaque, const uint8_t *buf, int size)
+ {
+ NetSocketState *s = opaque;
+ sendto(s->fd, buf, size, 0,
+ (struct sockaddr *)&s->dgram_dst, sizeof(s->dgram_dst));
+ }
+
+ static void net_socket_send(void *opaque)
+ {
+ NetSocketState *s = opaque;
+ int l, size, err;
+ uint8_t buf1[4096];
+ const uint8_t *buf;
+
+ size = recv(s->fd, buf1, sizeof(buf1), 0);
+ if (size < 0) {
+ err = socket_error();
+ if (err != EWOULDBLOCK)
+ goto eoc;
+ } else if (size == 0) {
+ /* end of connection */
+ eoc:
+ qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+ closesocket(s->fd);
+ return;
+ }
+ buf = buf1;
+ while (size > 0) {
+ /* reassemble a packet from the network */
+ switch(s->state) {
+ case 0:
+ l = 4 - s->index;
+ if (l > size)
+ l = size;
+ memcpy(s->buf + s->index, buf, l);
+ buf += l;
+ size -= l;
+ s->index += l;
+ if (s->index == 4) {
+ /* got length */
+ s->packet_len = ntohl(*(uint32_t *)s->buf);
+ s->index = 0;
+ s->state = 1;
+ }
+ break;
+ case 1:
+ l = s->packet_len - s->index;
+ if (l > size)
+ l = size;
+ memcpy(s->buf + s->index, buf, l);
+ s->index += l;
+ buf += l;
+ size -= l;
+ if (s->index >= s->packet_len) {
+ qemu_send_packet(s->vc, s->buf, s->packet_len);
+ s->index = 0;
+ s->state = 0;
+ }
+ break;
+ }
+ }
+ }
+
+ static void net_socket_send_dgram(void *opaque)
+ {
+ NetSocketState *s = opaque;
+ int size;
+
+ size = recv(s->fd, s->buf, sizeof(s->buf), 0);
+ if (size < 0)
+ return;
+ if (size == 0) {
+ /* end of connection */
+ qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+ return;
+ }
+ qemu_send_packet(s->vc, s->buf, size);
+ }
+
+ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr)
+ {
+ struct ip_mreq imr;
+ int fd;
+ int val, ret;
+ if (!IN_MULTICAST(ntohl(mcastaddr->sin_addr.s_addr))) {
+ fprintf(stderr, "qemu: error: specified mcastaddr \"%s\" (0x%08x) does not contain a multicast address\n",
+ inet_ntoa(mcastaddr->sin_addr),
+ (int)ntohl(mcastaddr->sin_addr.s_addr));
+ return -1;
+
+ }
+ fd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ perror("socket(PF_INET, SOCK_DGRAM)");
+ return -1;
+ }
+
+ val = 1;
+ ret=setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (const char *)&val, sizeof(val));
+ if (ret < 0) {
+ perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)");
+ goto fail;
+ }
+
+ ret = bind(fd, (struct sockaddr *)mcastaddr, sizeof(*mcastaddr));
+ if (ret < 0) {
+ perror("bind");
+ goto fail;
+ }
+
+ /* Add host to multicast group */
+ imr.imr_multiaddr = mcastaddr->sin_addr;
+ imr.imr_interface.s_addr = htonl(INADDR_ANY);
+
+ ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (const char *)&imr, sizeof(struct ip_mreq));
+ if (ret < 0) {
+ perror("setsockopt(IP_ADD_MEMBERSHIP)");
+ goto fail;
+ }
+
+ /* Force mcast msgs to loopback (eg. several QEMUs in same host */
+ val = 1;
+ ret=setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
+ (const char *)&val, sizeof(val));
+ if (ret < 0) {
+ perror("setsockopt(SOL_IP, IP_MULTICAST_LOOP)");
+ goto fail;
+ }
+
+ socket_set_nonblock(fd);
+ return fd;
+ fail:
+ if (fd >= 0)
+ closesocket(fd);
+ return -1;
+ }
+
+ static NetSocketState *net_socket_fd_init_dgram(VLANState *vlan,
+ const char *model,
+ const char *name,
+ int fd, int is_connected)
+ {
+ struct sockaddr_in saddr;
+ int newfd;
+ socklen_t saddr_len;
+ NetSocketState *s;
+
+ /* fd passed: multicast: "learn" dgram_dst address from bound address and save it
+ * Because this may be "shared" socket from a "master" process, datagrams would be recv()
+ * by ONLY ONE process: we must "clone" this dgram socket --jjo
+ */
+
+ if (is_connected) {
+ if (getsockname(fd, (struct sockaddr *) &saddr, &saddr_len) == 0) {
+ /* must be bound */
+ if (saddr.sin_addr.s_addr==0) {
+ fprintf(stderr, "qemu: error: init_dgram: fd=%d unbound, cannot setup multicast dst addr\n",
+ fd);
+ return NULL;
+ }
+ /* clone dgram socket */
+ newfd = net_socket_mcast_create(&saddr);
+ if (newfd < 0) {
+ /* error already reported by net_socket_mcast_create() */
+ close(fd);
+ return NULL;
+ }
+ /* clone newfd to fd, close newfd */
+ dup2(newfd, fd);
+ close(newfd);
+
+ } else {
+ fprintf(stderr, "qemu: error: init_dgram: fd=%d failed getsockname(): %s\n",
+ fd, strerror(errno));
+ return NULL;
+ }
+ }
+
+ s = qemu_mallocz(sizeof(NetSocketState));
+ if (!s)
+ return NULL;
+ s->fd = fd;
+
+ s->vc = qemu_new_vlan_client(vlan, model, name, net_socket_receive_dgram, NULL, s);
+ qemu_set_fd_handler(s->fd, net_socket_send_dgram, NULL, s);
+
+ /* mcast: save bound address as dst */
+ if (is_connected) s->dgram_dst=saddr;
+
+ snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+ "socket: fd=%d (%s mcast=%s:%d)",
+ fd, is_connected? "cloned" : "",
+ inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
+ return s;
+ }
+
+ static void net_socket_connect(void *opaque)
+ {
+ NetSocketState *s = opaque;
+ qemu_set_fd_handler(s->fd, net_socket_send, NULL, s);
+ }
+
+ static NetSocketState *net_socket_fd_init_stream(VLANState *vlan,
+ const char *model,
+ const char *name,
+ int fd, int is_connected)
+ {
+ NetSocketState *s;
+ s = qemu_mallocz(sizeof(NetSocketState));
+ if (!s)
+ return NULL;
+ s->fd = fd;
+ s->vc = qemu_new_vlan_client(vlan, model, name,
+ net_socket_receive, NULL, s);
+ snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+ "socket: fd=%d", fd);
+ if (is_connected) {
+ net_socket_connect(s);
+ } else {
+ qemu_set_fd_handler(s->fd, NULL, net_socket_connect, s);
+ }
+ return s;
+ }
+
+ static NetSocketState *net_socket_fd_init(VLANState *vlan,
+ const char *model, const char *name,
+ int fd, int is_connected)
+ {
+ int so_type=-1, optlen=sizeof(so_type);
+
+ if(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&so_type,
+ (socklen_t *)&optlen)< 0) {
+ fprintf(stderr, "qemu: error: getsockopt(SO_TYPE) for fd=%d failed\n", fd);
+ return NULL;
+ }
+ switch(so_type) {
+ case SOCK_DGRAM:
+ return net_socket_fd_init_dgram(vlan, model, name, fd, is_connected);
+ case SOCK_STREAM:
+ return net_socket_fd_init_stream(vlan, model, name, fd, is_connected);
+ default:
+ /* who knows ... this could be a eg. a pty, do warn and continue as stream */
+ fprintf(stderr, "qemu: warning: socket type=%d for fd=%d is not SOCK_DGRAM or SOCK_STREAM\n", so_type, fd);
+ return net_socket_fd_init_stream(vlan, model, name, fd, is_connected);
+ }
+ return NULL;
+ }
+
+ static void net_socket_accept(void *opaque)
+ {
+ NetSocketListenState *s = opaque;
+ NetSocketState *s1;
+ struct sockaddr_in saddr;
+ socklen_t len;
+ int fd;
+
+ for(;;) {
+ len = sizeof(saddr);
+ fd = accept(s->fd, (struct sockaddr *)&saddr, &len);
+ if (fd < 0 && errno != EINTR) {
+ return;
+ } else if (fd >= 0) {
+ break;
+ }
+ }
+ s1 = net_socket_fd_init(s->vlan, s->model, s->name, fd, 1);
+ if (!s1) {
+ closesocket(fd);
+ } else {
+ snprintf(s1->vc->info_str, sizeof(s1->vc->info_str),
+ "socket: connection from %s:%d",
+ inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
+ }
+ }
+
+ static int net_socket_listen_init(VLANState *vlan,
+ const char *model,
+ const char *name,
+ const char *host_str)
+ {
+ NetSocketListenState *s;
+ int fd, val, ret;
+ struct sockaddr_in saddr;
+
+ if (parse_host_port(&saddr, host_str) < 0)
+ return -1;
+
+ s = qemu_mallocz(sizeof(NetSocketListenState));
+ if (!s)
+ return -1;
+
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("socket");
+ return -1;
+ }
+ socket_set_nonblock(fd);
+
+ /* allow fast reuse */
+ val = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val));
+
+ ret = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr));
+ if (ret < 0) {
+ perror("bind");
+ return -1;
+ }
+ ret = listen(fd, 0);
+ if (ret < 0) {
+ perror("listen");
+ return -1;
+ }
+ s->vlan = vlan;
+ s->model = strdup(model);
+ s->name = strdup(name);
+ s->fd = fd;
+ qemu_set_fd_handler(fd, net_socket_accept, NULL, s);
+ return 0;
+ }
+
+ static int net_socket_connect_init(VLANState *vlan,
+ const char *model,
+ const char *name,
+ const char *host_str)
+ {
+ NetSocketState *s;
+ int fd, connected, ret, err;
+ struct sockaddr_in saddr;
+
+ if (parse_host_port(&saddr, host_str) < 0)
+ return -1;
+
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("socket");
+ return -1;
+ }
+ socket_set_nonblock(fd);
+
+ connected = 0;
+ for(;;) {
+ ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr));
+ if (ret < 0) {
+ err = socket_error();
+ if (err == EINTR || err == EWOULDBLOCK) {
+ } else if (err == EINPROGRESS) {
+ break;
+ #ifdef _WIN32
+ } else if (err == WSAEALREADY) {
+ break;
+ #endif
+ } else {
+ perror("connect");
+ closesocket(fd);
+ return -1;
+ }
+ } else {
+ connected = 1;
+ break;
+ }
+ }
+ s = net_socket_fd_init(vlan, model, name, fd, connected);
+ if (!s)
+ return -1;
+ snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+ "socket: connect to %s:%d",
+ inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
+ return 0;
+ }
+
+ static int net_socket_mcast_init(VLANState *vlan,
+ const char *model,
+ const char *name,
+ const char *host_str)
+ {
+ NetSocketState *s;
+ int fd;
+ struct sockaddr_in saddr;
+
+ if (parse_host_port(&saddr, host_str) < 0)
+ return -1;
+
+
+ fd = net_socket_mcast_create(&saddr);
+ if (fd < 0)
+ return -1;
+
+ s = net_socket_fd_init(vlan, model, name, fd, 0);
+ if (!s)
+ return -1;
+
+ s->dgram_dst = saddr;
+
+ snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+ "socket: mcast=%s:%d",
+ inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
+ return 0;
+
+ }
+
+ /* find or alloc a new VLAN */
+ VLANState *qemu_find_vlan(int id)
+ {
+ VLANState **pvlan, *vlan;
+ for(vlan = first_vlan; vlan != NULL; vlan = vlan->next) {
+ if (vlan->id == id)
+ return vlan;
+ }
+ vlan = qemu_mallocz(sizeof(VLANState));
+ if (!vlan)
+ return NULL;
+ vlan->id = id;
+ vlan->next = NULL;
+ pvlan = &first_vlan;
+ while (*pvlan != NULL)
+ pvlan = &(*pvlan)->next;
+ *pvlan = vlan;
+ return vlan;
+ }
+
+ void qemu_check_nic_model(NICInfo *nd, const char *model)
+ {
+ const char *models[2];
+
+ models[0] = model;
+ models[1] = NULL;
+
+ qemu_check_nic_model_list(nd, models, model);
+ }
+
+ void qemu_check_nic_model_list(NICInfo *nd, const char * const *models,
+ const char *default_model)
+ {
+ int i, exit_status = 0;
+
+ if (!nd->model)
+ nd->model = strdup(default_model);
+
+ if (strcmp(nd->model, "?") != 0) {
+ for (i = 0 ; models[i]; i++)
+ if (strcmp(nd->model, models[i]) == 0)
+ return;
+
+ fprintf(stderr, "qemu: Unsupported NIC model: %s\n", nd->model);
+ exit_status = 1;
+ }
+
+ fprintf(stderr, "qemu: Supported NIC models: ");
+ for (i = 0 ; models[i]; i++)
+ fprintf(stderr, "%s%c", models[i], models[i+1] ? ',' : '\n');
+
+ exit(exit_status);
+ }
+
+ int net_client_init(const char *device, const char *p)
+ {
+ char buf[1024];
+ int vlan_id, ret;
+ VLANState *vlan;
+ char *name = NULL;
+
+ vlan_id = 0;
+ if (get_param_value(buf, sizeof(buf), "vlan", p)) {
+ vlan_id = strtol(buf, NULL, 0);
+ }
+ vlan = qemu_find_vlan(vlan_id);
+ if (!vlan) {
+ fprintf(stderr, "Could not create vlan %d\n", vlan_id);
+ return -1;
+ }
+ if (get_param_value(buf, sizeof(buf), "name", p)) {
+ name = strdup(buf);
+ }
+ if (!strcmp(device, "nic")) {
+ NICInfo *nd;
+ uint8_t *macaddr;
+
+ if (nb_nics >= MAX_NICS) {
+ fprintf(stderr, "Too Many NICs\n");
+ return -1;
+ }
+ nd = &nd_table[nb_nics];
+ macaddr = nd->macaddr;
+ macaddr[0] = 0x52;
+ macaddr[1] = 0x54;
+ macaddr[2] = 0x00;
+ macaddr[3] = 0x12;
+ macaddr[4] = 0x34;
+ macaddr[5] = 0x56 + nb_nics;
+
+ if (get_param_value(buf, sizeof(buf), "macaddr", p)) {
+ if (parse_macaddr(macaddr, buf) < 0) {
+ fprintf(stderr, "invalid syntax for ethernet address\n");
+ return -1;
+ }
+ }
+ if (get_param_value(buf, sizeof(buf), "model", p)) {
+ nd->model = strdup(buf);
+ }
+ nd->vlan = vlan;
+ nd->name = name;
+ name = NULL;
+ nb_nics++;
+ vlan->nb_guest_devs++;
+ ret = 0;
+ } else
+ if (!strcmp(device, "none")) {
+ /* does nothing. It is needed to signal that no network cards
+ are wanted */
+ ret = 0;
+ } else
+ #ifdef CONFIG_SLIRP
+ if (!strcmp(device, "user")) {
+ if (get_param_value(buf, sizeof(buf), "hostname", p)) {
+ pstrcpy(slirp_hostname, sizeof(slirp_hostname), buf);
+ }
+ if (get_param_value(buf, sizeof(buf), "restrict", p)) {
+ slirp_restrict = (buf[0] == 'y') ? 1 : 0;
+ }
+ if (get_param_value(buf, sizeof(buf), "ip", p)) {
+ slirp_ip = strdup(buf);
+ }
+ vlan->nb_host_devs++;
+ ret = net_slirp_init(vlan, device, name);
+ } else
+ #endif
+ #ifdef _WIN32
+ if (!strcmp(device, "tap")) {
+ char ifname[64];
+ if (get_param_value(ifname, sizeof(ifname), "ifname", p) <= 0) {
+ fprintf(stderr, "tap: no interface name\n");
+ return -1;
+ }
+ vlan->nb_host_devs++;
+ ret = tap_win32_init(vlan, device, name, ifname);
+ } else
+ #elif defined (_AIX)
+ #else
+ if (!strcmp(device, "tap")) {
+ char ifname[64];
- ret = net_tap_init(vlan, device, name, ifname, setup_script, down_script);
++ char setup_script[1024], down_script[1024], script_arg[1024];
+ int fd;
+ vlan->nb_host_devs++;
+ if (get_param_value(buf, sizeof(buf), "fd", p) > 0) {
+ fd = strtol(buf, NULL, 0);
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+ ret = -1;
+ if (net_tap_fd_init(vlan, device, name, fd))
+ ret = 0;
+ } else {
+ if (get_param_value(ifname, sizeof(ifname), "ifname", p) <= 0) {
+ ifname[0] = '\0';
+ }
+ if (get_param_value(setup_script, sizeof(setup_script), "script", p) == 0) {
+ pstrcpy(setup_script, sizeof(setup_script), DEFAULT_NETWORK_SCRIPT);
+ }
+ if (get_param_value(down_script, sizeof(down_script), "downscript", p) == 0) {
+ pstrcpy(down_script, sizeof(down_script), DEFAULT_NETWORK_DOWN_SCRIPT);
+ }
++ if (get_param_value(script_arg, sizeof(script_arg), "scriptarg", p) == 0 &&
++ get_param_value(script_arg, sizeof(script_arg), "bridge", p) == 0) { /* deprecated; for xend compatibility */
+++ pstrcpy(script_arg, sizeof(script_arg), "");
++ }
++ ret = net_tap_init(vlan, device, name, ifname, setup_script, down_script, script_arg);
+ }
+ } else
+ #endif
+ if (!strcmp(device, "socket")) {
+ if (get_param_value(buf, sizeof(buf), "fd", p) > 0) {
+ int fd;
+ fd = strtol(buf, NULL, 0);
+ ret = -1;
+ if (net_socket_fd_init(vlan, device, name, fd, 1))
+ ret = 0;
+ } else if (get_param_value(buf, sizeof(buf), "listen", p) > 0) {
+ ret = net_socket_listen_init(vlan, device, name, buf);
+ } else if (get_param_value(buf, sizeof(buf), "connect", p) > 0) {
+ ret = net_socket_connect_init(vlan, device, name, buf);
+ } else if (get_param_value(buf, sizeof(buf), "mcast", p) > 0) {
+ ret = net_socket_mcast_init(vlan, device, name, buf);
+ } else {
+ fprintf(stderr, "Unknown socket options: %s\n", p);
+ return -1;
+ }
+ vlan->nb_host_devs++;
+ } else
+ #ifdef CONFIG_VDE
+ if (!strcmp(device, "vde")) {
+ char vde_sock[1024], vde_group[512];
+ int vde_port, vde_mode;
+ vlan->nb_host_devs++;
+ if (get_param_value(vde_sock, sizeof(vde_sock), "sock", p) <= 0) {
+ vde_sock[0] = '\0';
+ }
+ if (get_param_value(buf, sizeof(buf), "port", p) > 0) {
+ vde_port = strtol(buf, NULL, 10);
+ } else {
+ vde_port = 0;
+ }
+ if (get_param_value(vde_group, sizeof(vde_group), "group", p) <= 0) {
+ vde_group[0] = '\0';
+ }
+ if (get_param_value(buf, sizeof(buf), "mode", p) > 0) {
+ vde_mode = strtol(buf, NULL, 8);
+ } else {
+ vde_mode = 0700;
+ }
+ ret = net_vde_init(vlan, device, name, vde_sock, vde_port, vde_group, vde_mode);
+ } else
+ #endif
+ {
+ fprintf(stderr, "Unknown network device: %s\n", device);
+ if (name)
+ free(name);
+ return -1;
+ }
+ if (ret < 0) {
+ fprintf(stderr, "Could not initialize device '%s'\n", device);
+ }
+ if (name)
+ free(name);
+ return ret;
+ }
+
+ int net_client_parse(const char *str)
+ {
+ const char *p;
+ char *q;
+ char device[64];
+
+ p = str;
+ q = device;
+ while (*p != '\0' && *p != ',') {
+ if ((q - device) < sizeof(device) - 1)
+ *q++ = *p;
+ p++;
+ }
+ *q = '\0';
+ if (*p == ',')
+ p++;
+
+ return net_client_init(device, p);
+ }
+
+ void do_info_network(void)
+ {
+ VLANState *vlan;
+ VLANClientState *vc;
+
+ for(vlan = first_vlan; vlan != NULL; vlan = vlan->next) {
+ term_printf("VLAN %d devices:\n", vlan->id);
+ for(vc = vlan->first_client; vc != NULL; vc = vc->next)
+ term_printf(" %s: %s\n", vc->name, vc->info_str);
+ }
+ }
+
+ int do_set_link(const char *name, const char *up_or_down)
+ {
+ VLANState *vlan;
+ VLANClientState *vc = NULL;
+
+ for (vlan = first_vlan; vlan != NULL; vlan = vlan->next)
+ for (vc = vlan->first_client; vc != NULL; vc = vc->next)
+ if (strcmp(vc->name, name) == 0)
+ goto done;
+ done:
+
+ if (!vc) {
+ term_printf("could not find network device '%s'", name);
+ return 0;
+ }
+
+ if (strcmp(up_or_down, "up") == 0)
+ vc->link_down = 0;
+ else if (strcmp(up_or_down, "down") == 0)
+ vc->link_down = 1;
+ else
+ term_printf("invalid link status '%s'; only 'up' or 'down' valid\n",
+ up_or_down);
+
+ if (vc->link_status_changed)
+ vc->link_status_changed(vc);
+
+ return 1;
+ }
+
+ void net_cleanup(void)
+ {
+ VLANState *vlan;
+
+ #if !defined(_WIN32)
+ /* close network clients */
+ for(vlan = first_vlan; vlan != NULL; vlan = vlan->next) {
+ VLANClientState *vc;
+
+ for(vc = vlan->first_client; vc != NULL; vc = vc->next) {
+ if (vc->fd_read == tap_receive) {
+ TAPState *s = vc->opaque;
+
+ if (s->down_script[0])
+ launch_script(s->down_script, s->down_script_arg, s->fd);
+ }
+ #if defined(CONFIG_VDE)
+ if (vc->fd_read == vde_from_qemu) {
+ VDEState *s = vc->opaque;
+ vde_close(s->vde);
+ }
+ #endif
+ }
+ }
+ #endif
+ }
+
+ void net_client_check(void)
+ {
+ VLANState *vlan;
+
+ for(vlan = first_vlan; vlan != NULL; vlan = vlan->next) {
+ if (vlan->nb_guest_devs == 0 && vlan->nb_host_devs == 0)
+ continue;
+ if (vlan->nb_guest_devs == 0)
+ fprintf(stderr, "Warning: vlan %d with no nics\n", vlan->id);
+ if (vlan->nb_host_devs == 0)
+ fprintf(stderr,
+ "Warning: vlan %d is not connected to host network\n",
+ vlan->id);
+ }
+ }
uint8_t *addrs, uint8_t *buf);
void net_checksum_calculate(uint8_t *data, int length);
+ /* from net.c */
+ int net_client_init(const char *device, const char *p);
+ int net_client_parse(const char *str);
+ void net_slirp_smb(const char *exported_dir);
+ void net_slirp_redir(const char *redir_str);
+ void net_cleanup(void);
+ int slirp_is_inited(void);
+ void net_client_check(void);
+
++#ifndef DEFAULT_NETWORK_SCRIPT
+ #define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup"
+ #define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown"
++#endif
+ #ifdef __sun__
+ #define SMBD_COMMAND "/usr/sfw/sbin/smbd"
+ #else
+ #define SMBD_COMMAND "/usr/sbin/smbd"
+ #endif
+
#endif
--- /dev/null
-#ifndef _WIN32
+ /*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 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 "qemu-common.h"
+ #include "net.h"
+ #include "console.h"
+ #include "sysemu.h"
+ #include "qemu-timer.h"
+ #include "qemu-char.h"
+ #include "block.h"
+ #include "hw/usb.h"
+ #include "hw/baum.h"
+
+ #include <unistd.h>
+ #include <fcntl.h>
+ #include <signal.h>
+ #include <time.h>
+ #include <errno.h>
+ #include <sys/time.h>
+ #include <zlib.h>
+
+ #ifndef _WIN32
+ #include <sys/times.h>
+ #include <sys/wait.h>
+ #include <termios.h>
+ #include <sys/mman.h>
+ #include <sys/ioctl.h>
+ #include <sys/resource.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <net/if.h>
+ #ifdef __NetBSD__
+ #include <net/if_tap.h>
+ #endif
+ #ifdef __linux__
+ #include <linux/if_tun.h>
+ #endif
+ #include <arpa/inet.h>
+ #include <dirent.h>
+ #include <netdb.h>
+ #include <sys/select.h>
+ #ifdef _BSD
+ #include <sys/stat.h>
+ #ifdef __FreeBSD__
+ #include <libutil.h>
+ #include <dev/ppbus/ppi.h>
+ #include <dev/ppbus/ppbconf.h>
+ #else
+ #include <util.h>
+ #endif
+ #elif defined (__GLIBC__) && defined (__FreeBSD_kernel__)
+ #include <freebsd/stdlib.h>
+ #else
+ #ifdef __linux__
+ #include <pty.h>
+
+ #include <linux/ppdev.h>
+ #include <linux/parport.h>
+ #endif
+ #ifdef __sun__
+ #include <sys/stat.h>
+ #include <sys/ethernet.h>
+ #include <sys/sockio.h>
+ #include <netinet/arp.h>
+ #include <netinet/in.h>
+ #include <netinet/in_systm.h>
+ #include <netinet/ip.h>
+ #include <netinet/ip_icmp.h> // must come after ip.h
+ #include <netinet/udp.h>
+ #include <netinet/tcp.h>
+ #include <net/if.h>
+ #include <syslog.h>
+ #include <stropts.h>
+ #endif
+ #endif
+ #endif
+
+ #include "qemu_socket.h"
+
+ /***********************************************************/
+ /* character device */
+
+ static void qemu_chr_event(CharDriverState *s, int event)
+ {
+ if (!s->chr_event)
+ return;
+ s->chr_event(s->handler_opaque, event);
+ }
+
+ static void qemu_chr_reset_bh(void *opaque)
+ {
+ CharDriverState *s = opaque;
+ qemu_chr_event(s, CHR_EVENT_RESET);
+ qemu_bh_delete(s->bh);
+ s->bh = NULL;
+ }
+
+ void qemu_chr_reset(CharDriverState *s)
+ {
+ if (s->bh == NULL) {
+ s->bh = qemu_bh_new(qemu_chr_reset_bh, s);
+ qemu_bh_schedule(s->bh);
+ }
+ }
+
+ int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len)
+ {
+ return s->chr_write(s, buf, len);
+ }
+
+ int qemu_chr_ioctl(CharDriverState *s, int cmd, void *arg)
+ {
+ if (!s->chr_ioctl)
+ return -ENOTSUP;
+ return s->chr_ioctl(s, cmd, arg);
+ }
+
+ int qemu_chr_can_read(CharDriverState *s)
+ {
+ if (!s->chr_can_read)
+ return 0;
+ return s->chr_can_read(s->handler_opaque);
+ }
+
+ void qemu_chr_read(CharDriverState *s, uint8_t *buf, int len)
+ {
+ s->chr_read(s->handler_opaque, buf, len);
+ }
+
+ void qemu_chr_accept_input(CharDriverState *s)
+ {
+ if (s->chr_accept_input)
+ s->chr_accept_input(s);
+ }
+
+ void qemu_chr_printf(CharDriverState *s, const char *fmt, ...)
+ {
+ char buf[4096];
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ qemu_chr_write(s, (uint8_t *)buf, strlen(buf));
+ va_end(ap);
+ }
+
+ void qemu_chr_send_event(CharDriverState *s, int event)
+ {
+ if (s->chr_send_event)
+ s->chr_send_event(s, event);
+ }
+
+ void qemu_chr_add_handlers(CharDriverState *s,
+ IOCanRWHandler *fd_can_read,
+ IOReadHandler *fd_read,
+ IOEventHandler *fd_event,
+ void *opaque)
+ {
+ s->chr_can_read = fd_can_read;
+ s->chr_read = fd_read;
+ s->chr_event = fd_event;
+ s->handler_opaque = opaque;
+ if (s->chr_update_read_handler)
+ s->chr_update_read_handler(s);
+ }
+
+ static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+ {
+ return len;
+ }
+
+ static CharDriverState *qemu_chr_open_null(void)
+ {
+ CharDriverState *chr;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr)
+ return NULL;
+ chr->chr_write = null_chr_write;
+ return chr;
+ }
+
+ /* MUX driver for serial I/O splitting */
+ static int term_timestamps;
+ static int64_t term_timestamps_start;
+ #define MAX_MUX 4
+ #define MUX_BUFFER_SIZE 32 /* Must be a power of 2. */
+ #define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1)
+ typedef struct {
+ IOCanRWHandler *chr_can_read[MAX_MUX];
+ IOReadHandler *chr_read[MAX_MUX];
+ IOEventHandler *chr_event[MAX_MUX];
+ void *ext_opaque[MAX_MUX];
+ CharDriverState *drv;
+ unsigned char buffer[MUX_BUFFER_SIZE];
+ int prod;
+ int cons;
+ int mux_cnt;
+ int term_got_escape;
+ int max_size;
+ } MuxDriver;
+
+
+ static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+ {
+ MuxDriver *d = chr->opaque;
+ int ret;
+ if (!term_timestamps) {
+ ret = d->drv->chr_write(d->drv, buf, len);
+ } else {
+ int i;
+
+ ret = 0;
+ for(i = 0; i < len; i++) {
+ ret += d->drv->chr_write(d->drv, buf+i, 1);
+ if (buf[i] == '\n') {
+ char buf1[64];
+ int64_t ti;
+ int secs;
+
+ ti = qemu_get_clock(rt_clock);
+ if (term_timestamps_start == -1)
+ term_timestamps_start = ti;
+ ti -= term_timestamps_start;
+ secs = ti / 1000000000;
+ snprintf(buf1, sizeof(buf1),
+ "[%02d:%02d:%02d.%03d] ",
+ secs / 3600,
+ (secs / 60) % 60,
+ secs % 60,
+ (int)((ti / 1000000) % 1000));
+ d->drv->chr_write(d->drv, (uint8_t *)buf1, strlen(buf1));
+ }
+ }
+ }
+ return ret;
+ }
+
+ static const char * const mux_help[] = {
+ "% h print this help\n\r",
+ "% x exit emulator\n\r",
+ "% s save disk data back to file (if -snapshot)\n\r",
+ "% t toggle console timestamps\n\r"
+ "% b send break (magic sysrq)\n\r",
+ "% c switch between console and monitor\n\r",
+ "% % sends %\n\r",
+ NULL
+ };
+
+ int term_escape_char = 0x01; /* ctrl-a is used for escape */
+ static void mux_print_help(CharDriverState *chr)
+ {
+ int i, j;
+ char ebuf[15] = "Escape-Char";
+ char cbuf[50] = "\n\r";
+
+ if (term_escape_char > 0 && term_escape_char < 26) {
+ snprintf(cbuf, sizeof(cbuf), "\n\r");
+ snprintf(ebuf, sizeof(ebuf), "C-%c", term_escape_char - 1 + 'a');
+ } else {
+ snprintf(cbuf, sizeof(cbuf),
+ "\n\rEscape-Char set to Ascii: 0x%02x\n\r\n\r",
+ term_escape_char);
+ }
+ chr->chr_write(chr, (uint8_t *)cbuf, strlen(cbuf));
+ for (i = 0; mux_help[i] != NULL; i++) {
+ for (j=0; mux_help[i][j] != '\0'; j++) {
+ if (mux_help[i][j] == '%')
+ chr->chr_write(chr, (uint8_t *)ebuf, strlen(ebuf));
+ else
+ chr->chr_write(chr, (uint8_t *)&mux_help[i][j], 1);
+ }
+ }
+ }
+
+ static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch)
+ {
+ if (d->term_got_escape) {
+ d->term_got_escape = 0;
+ if (ch == term_escape_char)
+ goto send_char;
+ switch(ch) {
+ case '?':
+ case 'h':
+ mux_print_help(chr);
+ break;
+ case 'x':
+ {
+ const char *term = "QEMU: Terminated\n\r";
+ chr->chr_write(chr,(uint8_t *)term,strlen(term));
+ exit(0);
+ break;
+ }
+ case 's':
+ {
+ int i;
+ for (i = 0; i < nb_drives; i++) {
+ bdrv_commit(drives_table[i].bdrv);
+ }
+ }
+ break;
+ case 'b':
+ qemu_chr_event(chr, CHR_EVENT_BREAK);
+ break;
+ case 'c':
+ /* Switch to the next registered device */
+ chr->focus++;
+ if (chr->focus >= d->mux_cnt)
+ chr->focus = 0;
+ break;
+ case 't':
+ term_timestamps = !term_timestamps;
+ term_timestamps_start = -1;
+ break;
+ }
+ } else if (ch == term_escape_char) {
+ d->term_got_escape = 1;
+ } else {
+ send_char:
+ return 1;
+ }
+ return 0;
+ }
+
+ static void mux_chr_accept_input(CharDriverState *chr)
+ {
+ int m = chr->focus;
+ MuxDriver *d = chr->opaque;
+
+ while (d->prod != d->cons &&
+ d->chr_can_read[m] &&
+ d->chr_can_read[m](d->ext_opaque[m])) {
+ d->chr_read[m](d->ext_opaque[m],
+ &d->buffer[d->cons++ & MUX_BUFFER_MASK], 1);
+ }
+ }
+
+ static int mux_chr_can_read(void *opaque)
+ {
+ CharDriverState *chr = opaque;
+ MuxDriver *d = chr->opaque;
+
+ if ((d->prod - d->cons) < MUX_BUFFER_SIZE)
+ return 1;
+ if (d->chr_can_read[chr->focus])
+ return d->chr_can_read[chr->focus](d->ext_opaque[chr->focus]);
+ return 0;
+ }
+
+ static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
+ {
+ CharDriverState *chr = opaque;
+ MuxDriver *d = chr->opaque;
+ int m = chr->focus;
+ int i;
+
+ mux_chr_accept_input (opaque);
+
+ for(i = 0; i < size; i++)
+ if (mux_proc_byte(chr, d, buf[i])) {
+ if (d->prod == d->cons &&
+ d->chr_can_read[m] &&
+ d->chr_can_read[m](d->ext_opaque[m]))
+ d->chr_read[m](d->ext_opaque[m], &buf[i], 1);
+ else
+ d->buffer[d->prod++ & MUX_BUFFER_MASK] = buf[i];
+ }
+ }
+
+ static void mux_chr_event(void *opaque, int event)
+ {
+ CharDriverState *chr = opaque;
+ MuxDriver *d = chr->opaque;
+ int i;
+
+ /* Send the event to all registered listeners */
+ for (i = 0; i < d->mux_cnt; i++)
+ if (d->chr_event[i])
+ d->chr_event[i](d->ext_opaque[i], event);
+ }
+
+ static void mux_chr_update_read_handler(CharDriverState *chr)
+ {
+ MuxDriver *d = chr->opaque;
+
+ if (d->mux_cnt >= MAX_MUX) {
+ fprintf(stderr, "Cannot add I/O handlers, MUX array is full\n");
+ return;
+ }
+ d->ext_opaque[d->mux_cnt] = chr->handler_opaque;
+ d->chr_can_read[d->mux_cnt] = chr->chr_can_read;
+ d->chr_read[d->mux_cnt] = chr->chr_read;
+ d->chr_event[d->mux_cnt] = chr->chr_event;
+ /* Fix up the real driver with mux routines */
+ if (d->mux_cnt == 0) {
+ qemu_chr_add_handlers(d->drv, mux_chr_can_read, mux_chr_read,
+ mux_chr_event, chr);
+ }
+ chr->focus = d->mux_cnt;
+ d->mux_cnt++;
+ }
+
+ static CharDriverState *qemu_chr_open_mux(CharDriverState *drv)
+ {
+ CharDriverState *chr;
+ MuxDriver *d;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr)
+ return NULL;
+ d = qemu_mallocz(sizeof(MuxDriver));
+ if (!d) {
+ free(chr);
+ return NULL;
+ }
+
+ chr->opaque = d;
+ d->drv = drv;
+ chr->focus = -1;
+ chr->chr_write = mux_chr_write;
+ chr->chr_update_read_handler = mux_chr_update_read_handler;
+ chr->chr_accept_input = mux_chr_accept_input;
+ return chr;
+ }
+
+
+ #ifdef _WIN32
+ int send_all(int fd, const void *buf, int len1)
+ {
+ int ret, len;
+
+ len = len1;
+ while (len > 0) {
+ ret = send(fd, buf, len, 0);
+ if (ret < 0) {
+ errno = WSAGetLastError();
+ if (errno != WSAEWOULDBLOCK) {
+ return -1;
+ }
+ } else if (ret == 0) {
+ break;
+ } else {
+ buf += ret;
+ len -= ret;
+ }
+ }
+ return len1 - len;
+ }
+
+ #else
+
+ int send_all(int fd, const void *buf, int len1)
+ {
+ return qemu_write(fd, buf, len1);
+ }
+ #endif /* !_WIN32 */
+
+ #ifndef _WIN32
+
+ typedef struct {
+ int fd_in, fd_out;
+ int max_size;
+ } FDCharDriver;
+
+ #define STDIO_MAX_CLIENTS 1
+ static int stdio_nb_clients = 0;
+
+ static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+ {
+ FDCharDriver *s = chr->opaque;
+ return send_all(s->fd_out, buf, len);
+ }
+
+ static int fd_chr_read_poll(void *opaque)
+ {
+ CharDriverState *chr = opaque;
+ FDCharDriver *s = chr->opaque;
+
+ s->max_size = qemu_chr_can_read(chr);
+ return s->max_size;
+ }
+
+ static void fd_chr_read(void *opaque)
+ {
+ CharDriverState *chr = opaque;
+ FDCharDriver *s = chr->opaque;
+ int size, len;
+ uint8_t buf[1024];
+
+ len = sizeof(buf);
+ if (len > s->max_size)
+ len = s->max_size;
+ if (len == 0)
+ return;
+ size = read(s->fd_in, buf, len);
+ if (size == 0) {
+ /* FD has been closed. Remove it from the active list. */
+ qemu_set_fd_handler2(s->fd_in, NULL, NULL, NULL, NULL);
+ return;
+ }
+ if (size > 0) {
+ qemu_chr_read(chr, buf, size);
+ }
+ }
+
+ static void fd_chr_update_read_handler(CharDriverState *chr)
+ {
+ FDCharDriver *s = chr->opaque;
+
+ if (s->fd_in >= 0) {
+ if (nographic && s->fd_in == 0) {
+ } else {
+ qemu_set_fd_handler2(s->fd_in, fd_chr_read_poll,
+ fd_chr_read, NULL, chr);
+ }
+ }
+ }
+
+ static void fd_chr_close(struct CharDriverState *chr)
+ {
+ FDCharDriver *s = chr->opaque;
+
+ if (s->fd_in >= 0) {
+ if (nographic && s->fd_in == 0) {
+ } else {
+ qemu_set_fd_handler2(s->fd_in, NULL, NULL, NULL, NULL);
+ }
+ }
+
+ qemu_free(s);
+ }
+
+ /* open a character device to a unix fd */
+ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out)
+ {
+ CharDriverState *chr;
+ FDCharDriver *s;
+
+ socket_set_nonblock(fd_in);
+ socket_set_nonblock(fd_out);
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr)
+ return NULL;
+ s = qemu_mallocz(sizeof(FDCharDriver));
+ if (!s) {
+ free(chr);
+ return NULL;
+ }
+ s->fd_in = fd_in;
+ s->fd_out = fd_out;
+ chr->opaque = s;
+ chr->chr_write = fd_chr_write;
+ chr->chr_update_read_handler = fd_chr_update_read_handler;
+ chr->chr_close = fd_chr_close;
+
+ qemu_chr_reset(chr);
+
+ return chr;
+ }
+
+ static CharDriverState *qemu_chr_open_file_out(const char *file_out)
+ {
+ int fd_out;
+
+ TFR(fd_out = open(file_out, O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0666));
+ if (fd_out < 0)
+ return NULL;
+ return qemu_chr_open_fd(-1, fd_out);
+ }
+
+ static CharDriverState *qemu_chr_open_pipe(const char *filename)
+ {
+ int fd_in, fd_out;
+ char filename_in[256], filename_out[256];
+
+ snprintf(filename_in, 256, "%s.in", filename);
+ snprintf(filename_out, 256, "%s.out", filename);
+ TFR(fd_in = open(filename_in, O_RDWR | O_BINARY));
+ TFR(fd_out = open(filename_out, O_RDWR | O_BINARY));
+ if (fd_in < 0 || fd_out < 0) {
+ if (fd_in >= 0)
+ close(fd_in);
+ if (fd_out >= 0)
+ close(fd_out);
+ TFR(fd_in = fd_out = open(filename, O_RDWR | O_BINARY));
+ if (fd_in < 0)
+ return NULL;
+ }
+ return qemu_chr_open_fd(fd_in, fd_out);
+ }
+
+
+ /* for STDIO, we handle the case where several clients use it
+ (nographic mode) */
+
+ #define TERM_FIFO_MAX_SIZE 1
+
+ static uint8_t term_fifo[TERM_FIFO_MAX_SIZE];
+ static int term_fifo_size;
+
+ static int stdio_read_poll(void *opaque)
+ {
+ CharDriverState *chr = opaque;
+
+ /* try to flush the queue if needed */
+ if (term_fifo_size != 0 && qemu_chr_can_read(chr) > 0) {
+ qemu_chr_read(chr, term_fifo, 1);
+ term_fifo_size = 0;
+ }
+ /* see if we can absorb more chars */
+ if (term_fifo_size == 0)
+ return 1;
+ else
+ return 0;
+ }
+
+ static void stdio_read(void *opaque)
+ {
+ int size;
+ uint8_t buf[1];
+ CharDriverState *chr = opaque;
+
+ size = read(0, buf, 1);
+ if (size == 0) {
+ /* stdin has been closed. Remove it from the active list. */
+ qemu_set_fd_handler2(0, NULL, NULL, NULL, NULL);
+ return;
+ }
+ if (size > 0) {
+ if (qemu_chr_can_read(chr) > 0) {
+ qemu_chr_read(chr, buf, 1);
+ } else if (term_fifo_size == 0) {
+ term_fifo[term_fifo_size++] = buf[0];
+ }
+ }
+ }
+
+ /* init terminal so that we can grab keys */
+ static struct termios oldtty;
+ static int old_fd0_flags;
+ static int term_atexit_done;
+
+ static void term_exit(void)
+ {
+ tcsetattr (0, TCSANOW, &oldtty);
+ fcntl(0, F_SETFL, old_fd0_flags);
+ }
+
+ static void term_init(void)
+ {
+ struct termios 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 graphical mode, we allow Ctrl-C handling */
+ if (nographic)
+ 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);
+
+ if (!term_atexit_done++)
+ atexit(term_exit);
+
+ fcntl(0, F_SETFL, O_NONBLOCK);
+ }
+
+ static void qemu_chr_close_stdio(struct CharDriverState *chr)
+ {
+ term_exit();
+ stdio_nb_clients--;
+ qemu_set_fd_handler2(0, NULL, NULL, NULL, NULL);
+ fd_chr_close(chr);
+ }
+
+ static CharDriverState *qemu_chr_open_stdio(void)
+ {
+ CharDriverState *chr;
+
+ if (stdio_nb_clients >= STDIO_MAX_CLIENTS)
+ return NULL;
+ chr = qemu_chr_open_fd(0, 1);
+ chr->chr_close = qemu_chr_close_stdio;
+ qemu_set_fd_handler2(0, stdio_read_poll, stdio_read, NULL, chr);
+ stdio_nb_clients++;
+ term_init();
+
+ return chr;
+ }
+
+ #ifdef __sun__
+ /* Once Solaris has openpty(), this is going to be removed. */
+ int openpty(int *amaster, int *aslave, char *name,
+ struct termios *termp, struct winsize *winp)
+ {
+ const char *slave;
+ int mfd = -1, sfd = -1;
+
+ *amaster = *aslave = -1;
+
+ mfd = open("/dev/ptmx", O_RDWR | O_NOCTTY);
+ if (mfd < 0)
+ goto err;
+
+ if (grantpt(mfd) == -1 || unlockpt(mfd) == -1)
+ goto err;
+
+ if ((slave = ptsname(mfd)) == NULL)
+ goto err;
+
+ if ((sfd = open(slave, O_RDONLY | O_NOCTTY)) == -1)
+ goto err;
+
+ if (ioctl(sfd, I_PUSH, "ptem") == -1 ||
+ (termp != NULL && tcgetattr(sfd, termp) < 0))
+ goto err;
+
+ if (amaster)
+ *amaster = mfd;
+ if (aslave)
+ *aslave = sfd;
+ if (winp)
+ ioctl(sfd, TIOCSWINSZ, winp);
+
+ return 0;
+
+ err:
+ if (sfd != -1)
+ close(sfd);
+ close(mfd);
+ return -1;
+ }
+
+ void cfmakeraw (struct termios *termios_p)
+ {
+ termios_p->c_iflag &=
+ ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
+ termios_p->c_oflag &= ~OPOST;
+ termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
+ termios_p->c_cflag &= ~(CSIZE|PARENB);
+ termios_p->c_cflag |= CS8;
+
+ termios_p->c_cc[VMIN] = 0;
+ termios_p->c_cc[VTIME] = 0;
+ }
+ #endif
+
+ #if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
+ || defined(__NetBSD__) || defined(__OpenBSD__)
+
+ typedef struct {
+ int fd;
+ int connected;
+ int polling;
+ int read_bytes;
+ QEMUTimer *timer;
+ } PtyCharDriver;
+
+ static void pty_chr_update_read_handler(CharDriverState *chr);
+ static void pty_chr_state(CharDriverState *chr, int connected);
+
+ static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+ {
+ PtyCharDriver *s = chr->opaque;
+
+ if (!s->connected) {
+ /* guest sends data, check for (re-)connect */
+ pty_chr_update_read_handler(chr);
+ return 0;
+ }
+ return send_all(s->fd, buf, len);
+ }
+
+ static int pty_chr_read_poll(void *opaque)
+ {
+ CharDriverState *chr = opaque;
+ PtyCharDriver *s = chr->opaque;
+
+ s->read_bytes = qemu_chr_can_read(chr);
+ return s->read_bytes;
+ }
+
+ static void pty_chr_read(void *opaque)
+ {
+ CharDriverState *chr = opaque;
+ PtyCharDriver *s = chr->opaque;
+ int size, len;
+ uint8_t buf[1024];
+
+ len = sizeof(buf);
+ if (len > s->read_bytes)
+ len = s->read_bytes;
+ if (len == 0)
+ return;
+ size = read(s->fd, buf, len);
+ if ((size == -1 && errno == EIO) ||
+ (size == 0)) {
+ pty_chr_state(chr, 0);
+ return;
+ }
+ if (size > 0) {
+ pty_chr_state(chr, 1);
+ qemu_chr_read(chr, buf, size);
+ }
+ }
+
+ static void pty_chr_update_read_handler(CharDriverState *chr)
+ {
+ PtyCharDriver *s = chr->opaque;
+
+ qemu_set_fd_handler2(s->fd, pty_chr_read_poll,
+ pty_chr_read, NULL, chr);
+ s->polling = 1;
+ /*
+ * Short timeout here: just need wait long enougth that qemu makes
+ * it through the poll loop once. When reconnected we want a
+ * short timeout so we notice it almost instantly. Otherwise
+ * read() gives us -EIO instantly, making pty_chr_state() reset the
+ * timeout to the normal (much longer) poll interval before the
+ * timer triggers.
+ */
+ qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 10);
+ }
+
+ static void pty_chr_state(CharDriverState *chr, int connected)
+ {
+ PtyCharDriver *s = chr->opaque;
+
+ if (!connected) {
+ qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+ s->connected = 0;
+ s->polling = 0;
+ /* (re-)connect poll interval for idle guests: once per second.
+ * We check more frequently in case the guests sends data to
+ * the virtual device linked to our pty. */
+ qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 1000);
+ } else {
+ if (!s->connected)
+ qemu_chr_reset(chr);
+ s->connected = 1;
+ }
+ }
+
+ static void pty_chr_timer(void *opaque)
+ {
+ struct CharDriverState *chr = opaque;
+ PtyCharDriver *s = chr->opaque;
+
+ if (s->connected)
+ return;
+ if (s->polling) {
+ /* If we arrive here without polling being cleared due
+ * read returning -EIO, then we are (re-)connected */
+ pty_chr_state(chr, 1);
+ return;
+ }
+
+ /* Next poll ... */
+ pty_chr_update_read_handler(chr);
+ }
+
++static int pty_chr_getname(struct CharDriverState *chr, char *buf, size_t len) {
++ char *name;
++ FDCharDriver *s = chr->opaque;
++
++ name = ptsname(s->fd_in);
++ if (!name) return -1;
++ return snprintf(buf,len, "pty %s", name);
++}
++
+ static void pty_chr_close(struct CharDriverState *chr)
+ {
+ PtyCharDriver *s = chr->opaque;
+
+ qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+ close(s->fd);
+ qemu_free(s);
+ }
+
+ static CharDriverState *qemu_chr_open_pty(void)
+ {
+ CharDriverState *chr;
+ PtyCharDriver *s;
+ struct termios tty;
+ int slave_fd, len;
+ #if defined(__OpenBSD__)
+ char pty_name[PATH_MAX];
+ #define q_ptsname(x) pty_name
+ #else
+ char *pty_name = NULL;
+ #define q_ptsname(x) ptsname(x)
+ #endif
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr)
+ return NULL;
+ s = qemu_mallocz(sizeof(PtyCharDriver));
+ if (!s) {
+ qemu_free(chr);
+ return NULL;
+ }
+
+ if (openpty(&s->fd, &slave_fd, pty_name, NULL, NULL) < 0) {
+ return NULL;
+ }
+
+ /* Set raw attributes on the pty. */
+ tcgetattr(slave_fd, &tty);
+ cfmakeraw(&tty);
+ tcsetattr(slave_fd, TCSAFLUSH, &tty);
+ close(slave_fd);
+
+ len = strlen(q_ptsname(s->fd)) + 5;
+ chr->filename = qemu_malloc(len);
+ snprintf(chr->filename, len, "pty:%s", q_ptsname(s->fd));
+ fprintf(stderr, "char device redirected to %s\n", q_ptsname(s->fd));
+
+ chr->opaque = s;
+ chr->chr_write = pty_chr_write;
+ chr->chr_update_read_handler = pty_chr_update_read_handler;
+ chr->chr_close = pty_chr_close;
++ chr->chr_getname = pty_chr_getname;
+
+ s->timer = qemu_new_timer(rt_clock, pty_chr_timer, chr);
+
+ return chr;
+ }
+
+ static void tty_serial_init(int fd, int speed,
+ int parity, int data_bits, int stop_bits)
+ {
+ struct termios tty;
+ speed_t spd;
+
+ #if 0
+ printf("tty_serial_init: speed=%d parity=%c data=%d stop=%d\n",
+ speed, parity, data_bits, stop_bits);
+ #endif
+ tcgetattr (fd, &tty);
+
+ #define MARGIN 1.1
+ if (speed <= 50 * MARGIN)
+ spd = B50;
+ else if (speed <= 75 * MARGIN)
+ spd = B75;
+ else if (speed <= 300 * MARGIN)
+ spd = B300;
+ else if (speed <= 600 * MARGIN)
+ spd = B600;
+ else if (speed <= 1200 * MARGIN)
+ spd = B1200;
+ else if (speed <= 2400 * MARGIN)
+ spd = B2400;
+ else if (speed <= 4800 * MARGIN)
+ spd = B4800;
+ else if (speed <= 9600 * MARGIN)
+ spd = B9600;
+ else if (speed <= 19200 * MARGIN)
+ spd = B19200;
+ else if (speed <= 38400 * MARGIN)
+ spd = B38400;
+ else if (speed <= 57600 * MARGIN)
+ spd = B57600;
+ else if (speed <= 115200 * MARGIN)
+ spd = B115200;
+ else
+ spd = B115200;
+
+ cfsetispeed(&tty, spd);
+ cfsetospeed(&tty, spd);
+
+ tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
+ |INLCR|IGNCR|ICRNL|IXON);
+ tty.c_oflag |= OPOST;
+ tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN|ISIG);
+ tty.c_cflag &= ~(CSIZE|PARENB|PARODD|CRTSCTS|CSTOPB);
+ switch(data_bits) {
+ default:
+ case 8:
+ tty.c_cflag |= CS8;
+ break;
+ case 7:
+ tty.c_cflag |= CS7;
+ break;
+ case 6:
+ tty.c_cflag |= CS6;
+ break;
+ case 5:
+ tty.c_cflag |= CS5;
+ break;
+ }
+ switch(parity) {
+ default:
+ case 'N':
+ break;
+ case 'E':
+ tty.c_cflag |= PARENB;
+ break;
+ case 'O':
+ tty.c_cflag |= PARENB | PARODD;
+ break;
+ }
+ if (stop_bits == 2)
+ tty.c_cflag |= CSTOPB;
+
+ tcsetattr (fd, TCSANOW, &tty);
+ }
+
+ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg)
+ {
+ FDCharDriver *s = chr->opaque;
+
+ switch(cmd) {
+ case CHR_IOCTL_SERIAL_SET_PARAMS:
+ {
+ QEMUSerialSetParams *ssp = arg;
+ tty_serial_init(s->fd_in, ssp->speed, ssp->parity,
+ ssp->data_bits, ssp->stop_bits);
+ }
+ break;
+ case CHR_IOCTL_SERIAL_SET_BREAK:
+ {
+ int enable = *(int *)arg;
+ if (enable)
+ tcsendbreak(s->fd_in, 1);
+ }
+ break;
+ case CHR_IOCTL_SERIAL_GET_TIOCM:
+ {
+ int sarg = 0;
+ int *targ = (int *)arg;
+ ioctl(s->fd_in, TIOCMGET, &sarg);
+ *targ = 0;
+ if (sarg | TIOCM_CTS)
+ *targ |= CHR_TIOCM_CTS;
+ if (sarg | TIOCM_CAR)
+ *targ |= CHR_TIOCM_CAR;
+ if (sarg | TIOCM_DSR)
+ *targ |= CHR_TIOCM_DSR;
+ if (sarg | TIOCM_RI)
+ *targ |= CHR_TIOCM_RI;
+ if (sarg | TIOCM_DTR)
+ *targ |= CHR_TIOCM_DTR;
+ if (sarg | TIOCM_RTS)
+ *targ |= CHR_TIOCM_RTS;
+ }
+ break;
+ case CHR_IOCTL_SERIAL_SET_TIOCM:
+ {
+ int sarg = *(int *)arg;
+ int targ = 0;
+ if (sarg | CHR_TIOCM_DTR)
+ targ |= TIOCM_DTR;
+ if (sarg | CHR_TIOCM_RTS)
+ targ |= TIOCM_RTS;
+ ioctl(s->fd_in, TIOCMSET, &targ);
+ }
+ break;
+ default:
+ return -ENOTSUP;
+ }
+ return 0;
+ }
+
+ static CharDriverState *qemu_chr_open_tty(const char *filename)
+ {
+ CharDriverState *chr;
+ int fd;
+
+ TFR(fd = open(filename, O_RDWR | O_NONBLOCK));
+ tty_serial_init(fd, 115200, 'N', 8, 1);
+ chr = qemu_chr_open_fd(fd, fd);
+ if (!chr) {
+ close(fd);
+ return NULL;
+ }
+ chr->chr_ioctl = tty_serial_ioctl;
+ qemu_chr_reset(chr);
+ return chr;
+ }
+ #else /* ! __linux__ && ! __sun__ */
+ static CharDriverState *qemu_chr_open_pty(void)
+ {
+ return NULL;
+ }
+ #endif /* __linux__ || __sun__ */
+
+ #if defined(__linux__)
+ typedef struct {
+ int fd;
+ int mode;
+ } ParallelCharDriver;
+
+ static int pp_hw_mode(ParallelCharDriver *s, uint16_t mode)
+ {
+ if (s->mode != mode) {
+ int m = mode;
+ if (ioctl(s->fd, PPSETMODE, &m) < 0)
+ return 0;
+ s->mode = mode;
+ }
+ return 1;
+ }
+
+ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
+ {
+ ParallelCharDriver *drv = chr->opaque;
+ int fd = drv->fd;
+ uint8_t b;
+
+ switch(cmd) {
+ case CHR_IOCTL_PP_READ_DATA:
+ if (ioctl(fd, PPRDATA, &b) < 0)
+ return -ENOTSUP;
+ *(uint8_t *)arg = b;
+ break;
+ case CHR_IOCTL_PP_WRITE_DATA:
+ b = *(uint8_t *)arg;
+ if (ioctl(fd, PPWDATA, &b) < 0)
+ return -ENOTSUP;
+ break;
+ case CHR_IOCTL_PP_READ_CONTROL:
+ if (ioctl(fd, PPRCONTROL, &b) < 0)
+ return -ENOTSUP;
+ /* Linux gives only the lowest bits, and no way to know data
+ direction! For better compatibility set the fixed upper
+ bits. */
+ *(uint8_t *)arg = b | 0xc0;
+ break;
+ case CHR_IOCTL_PP_WRITE_CONTROL:
+ b = *(uint8_t *)arg;
+ if (ioctl(fd, PPWCONTROL, &b) < 0)
+ return -ENOTSUP;
+ break;
+ case CHR_IOCTL_PP_READ_STATUS:
+ if (ioctl(fd, PPRSTATUS, &b) < 0)
+ return -ENOTSUP;
+ *(uint8_t *)arg = b;
+ break;
+ case CHR_IOCTL_PP_DATA_DIR:
+ if (ioctl(fd, PPDATADIR, (int *)arg) < 0)
+ return -ENOTSUP;
+ break;
+ case CHR_IOCTL_PP_EPP_READ_ADDR:
+ if (pp_hw_mode(drv, IEEE1284_MODE_EPP|IEEE1284_ADDR)) {
+ struct ParallelIOArg *parg = arg;
+ int n = read(fd, parg->buffer, parg->count);
+ if (n != parg->count) {
+ return -EIO;
+ }
+ }
+ break;
+ case CHR_IOCTL_PP_EPP_READ:
+ if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
+ struct ParallelIOArg *parg = arg;
+ int n = read(fd, parg->buffer, parg->count);
+ if (n != parg->count) {
+ return -EIO;
+ }
+ }
+ break;
+ case CHR_IOCTL_PP_EPP_WRITE_ADDR:
+ if (pp_hw_mode(drv, IEEE1284_MODE_EPP|IEEE1284_ADDR)) {
+ struct ParallelIOArg *parg = arg;
+ int n = write(fd, parg->buffer, parg->count);
+ if (n != parg->count) {
+ return -EIO;
+ }
+ }
+ break;
+ case CHR_IOCTL_PP_EPP_WRITE:
+ if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
+ struct ParallelIOArg *parg = arg;
+ int n = write(fd, parg->buffer, parg->count);
+ if (n != parg->count) {
+ return -EIO;
+ }
+ }
+ break;
+ default:
+ return -ENOTSUP;
+ }
+ return 0;
+ }
+
+ static void pp_close(CharDriverState *chr)
+ {
+ ParallelCharDriver *drv = chr->opaque;
+ int fd = drv->fd;
+
+ pp_hw_mode(drv, IEEE1284_MODE_COMPAT);
+ ioctl(fd, PPRELEASE);
+ close(fd);
+ qemu_free(drv);
+ }
+
+ static CharDriverState *qemu_chr_open_pp(const char *filename)
+ {
+ CharDriverState *chr;
+ ParallelCharDriver *drv;
+ int fd;
+
+ TFR(fd = open(filename, O_RDWR));
+ if (fd < 0)
+ return NULL;
+
+ if (ioctl(fd, PPCLAIM) < 0) {
+ close(fd);
+ return NULL;
+ }
+
+ drv = qemu_mallocz(sizeof(ParallelCharDriver));
+ if (!drv) {
+ close(fd);
+ return NULL;
+ }
+ drv->fd = fd;
+ drv->mode = IEEE1284_MODE_COMPAT;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr) {
+ qemu_free(drv);
+ close(fd);
+ return NULL;
+ }
+ chr->chr_write = null_chr_write;
+ chr->chr_ioctl = pp_ioctl;
+ chr->chr_close = pp_close;
+ chr->opaque = drv;
+
+ qemu_chr_reset(chr);
+
+ return chr;
+ }
+ #endif /* __linux__ */
+
+ #if defined(__FreeBSD__)
+ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
+ {
+ int fd = (int)chr->opaque;
+ uint8_t b;
+
+ switch(cmd) {
+ case CHR_IOCTL_PP_READ_DATA:
+ if (ioctl(fd, PPIGDATA, &b) < 0)
+ return -ENOTSUP;
+ *(uint8_t *)arg = b;
+ break;
+ case CHR_IOCTL_PP_WRITE_DATA:
+ b = *(uint8_t *)arg;
+ if (ioctl(fd, PPISDATA, &b) < 0)
+ return -ENOTSUP;
+ break;
+ case CHR_IOCTL_PP_READ_CONTROL:
+ if (ioctl(fd, PPIGCTRL, &b) < 0)
+ return -ENOTSUP;
+ *(uint8_t *)arg = b;
+ break;
+ case CHR_IOCTL_PP_WRITE_CONTROL:
+ b = *(uint8_t *)arg;
+ if (ioctl(fd, PPISCTRL, &b) < 0)
+ return -ENOTSUP;
+ break;
+ case CHR_IOCTL_PP_READ_STATUS:
+ if (ioctl(fd, PPIGSTATUS, &b) < 0)
+ return -ENOTSUP;
+ *(uint8_t *)arg = b;
+ break;
+ default:
+ return -ENOTSUP;
+ }
+ return 0;
+ }
+
+ static CharDriverState *qemu_chr_open_pp(const char *filename)
+ {
+ CharDriverState *chr;
+ int fd;
+
+ fd = open(filename, O_RDWR);
+ if (fd < 0)
+ return NULL;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr) {
+ close(fd);
+ return NULL;
+ }
+ chr->opaque = (void *)fd;
+ chr->chr_write = null_chr_write;
+ chr->chr_ioctl = pp_ioctl;
+ return chr;
+ }
+ #endif
+
+ #else /* _WIN32 */
+
+ typedef struct {
+ int max_size;
+ HANDLE hcom, hrecv, hsend;
+ OVERLAPPED orecv, osend;
+ BOOL fpipe;
+ DWORD len;
+ } WinCharState;
+
+ #define NSENDBUF 2048
+ #define NRECVBUF 2048
+ #define MAXCONNECT 1
+ #define NTIMEOUT 5000
+
+ static int win_chr_poll(void *opaque);
+ static int win_chr_pipe_poll(void *opaque);
+
+ static void win_chr_close(CharDriverState *chr)
+ {
+ WinCharState *s = chr->opaque;
+
+ if (s->hsend) {
+ CloseHandle(s->hsend);
+ s->hsend = NULL;
+ }
+ if (s->hrecv) {
+ CloseHandle(s->hrecv);
+ s->hrecv = NULL;
+ }
+ if (s->hcom) {
+ CloseHandle(s->hcom);
+ s->hcom = NULL;
+ }
+ if (s->fpipe)
+ qemu_del_polling_cb(win_chr_pipe_poll, chr);
+ else
+ qemu_del_polling_cb(win_chr_poll, chr);
+ }
+
+ static int win_chr_init(CharDriverState *chr, const char *filename)
+ {
+ WinCharState *s = chr->opaque;
+ COMMCONFIG comcfg;
+ COMMTIMEOUTS cto = { 0, 0, 0, 0, 0};
+ COMSTAT comstat;
+ DWORD size;
+ DWORD err;
+
+ s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!s->hsend) {
+ fprintf(stderr, "Failed CreateEvent\n");
+ goto fail;
+ }
+ s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!s->hrecv) {
+ fprintf(stderr, "Failed CreateEvent\n");
+ goto fail;
+ }
+
+ s->hcom = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL,
+ OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
+ if (s->hcom == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "Failed CreateFile (%lu)\n", GetLastError());
+ s->hcom = NULL;
+ goto fail;
+ }
+
+ if (!SetupComm(s->hcom, NRECVBUF, NSENDBUF)) {
+ fprintf(stderr, "Failed SetupComm\n");
+ goto fail;
+ }
+
+ ZeroMemory(&comcfg, sizeof(COMMCONFIG));
+ size = sizeof(COMMCONFIG);
+ GetDefaultCommConfig(filename, &comcfg, &size);
+ comcfg.dcb.DCBlength = sizeof(DCB);
+ CommConfigDialog(filename, NULL, &comcfg);
+
+ if (!SetCommState(s->hcom, &comcfg.dcb)) {
+ fprintf(stderr, "Failed SetCommState\n");
+ goto fail;
+ }
+
+ if (!SetCommMask(s->hcom, EV_ERR)) {
+ fprintf(stderr, "Failed SetCommMask\n");
+ goto fail;
+ }
+
+ cto.ReadIntervalTimeout = MAXDWORD;
+ if (!SetCommTimeouts(s->hcom, &cto)) {
+ fprintf(stderr, "Failed SetCommTimeouts\n");
+ goto fail;
+ }
+
+ if (!ClearCommError(s->hcom, &err, &comstat)) {
+ fprintf(stderr, "Failed ClearCommError\n");
+ goto fail;
+ }
+ qemu_add_polling_cb(win_chr_poll, chr);
+ return 0;
+
+ fail:
+ win_chr_close(chr);
+ return -1;
+ }
+
+ static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1)
+ {
+ WinCharState *s = chr->opaque;
+ DWORD len, ret, size, err;
+
+ len = len1;
+ ZeroMemory(&s->osend, sizeof(s->osend));
+ s->osend.hEvent = s->hsend;
+ while (len > 0) {
+ if (s->hsend)
+ ret = WriteFile(s->hcom, buf, len, &size, &s->osend);
+ else
+ ret = WriteFile(s->hcom, buf, len, &size, NULL);
+ if (!ret) {
+ err = GetLastError();
+ if (err == ERROR_IO_PENDING) {
+ ret = GetOverlappedResult(s->hcom, &s->osend, &size, TRUE);
+ if (ret) {
+ buf += size;
+ len -= size;
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ } else {
+ buf += size;
+ len -= size;
+ }
+ }
+ return len1 - len;
+ }
+
+ static int win_chr_read_poll(CharDriverState *chr)
+ {
+ WinCharState *s = chr->opaque;
+
+ s->max_size = qemu_chr_can_read(chr);
+ return s->max_size;
+ }
+
+ static void win_chr_readfile(CharDriverState *chr)
+ {
+ WinCharState *s = chr->opaque;
+ int ret, err;
+ uint8_t buf[1024];
+ DWORD size;
+
+ ZeroMemory(&s->orecv, sizeof(s->orecv));
+ s->orecv.hEvent = s->hrecv;
+ ret = ReadFile(s->hcom, buf, s->len, &size, &s->orecv);
+ if (!ret) {
+ err = GetLastError();
+ if (err == ERROR_IO_PENDING) {
+ ret = GetOverlappedResult(s->hcom, &s->orecv, &size, TRUE);
+ }
+ }
+
+ if (size > 0) {
+ qemu_chr_read(chr, buf, size);
+ }
+ }
+
+ static void win_chr_read(CharDriverState *chr)
+ {
+ WinCharState *s = chr->opaque;
+
+ if (s->len > s->max_size)
+ s->len = s->max_size;
+ if (s->len == 0)
+ return;
+
+ win_chr_readfile(chr);
+ }
+
+ static int win_chr_poll(void *opaque)
+ {
+ CharDriverState *chr = opaque;
+ WinCharState *s = chr->opaque;
+ COMSTAT status;
+ DWORD comerr;
+
+ ClearCommError(s->hcom, &comerr, &status);
+ if (status.cbInQue > 0) {
+ s->len = status.cbInQue;
+ win_chr_read_poll(chr);
+ win_chr_read(chr);
+ return 1;
+ }
+ return 0;
+ }
+
+ static CharDriverState *qemu_chr_open_win(const char *filename)
+ {
+ CharDriverState *chr;
+ WinCharState *s;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr)
+ return NULL;
+ s = qemu_mallocz(sizeof(WinCharState));
+ if (!s) {
+ free(chr);
+ return NULL;
+ }
+ chr->opaque = s;
+ chr->chr_write = win_chr_write;
+ chr->chr_close = win_chr_close;
+
+ if (win_chr_init(chr, filename) < 0) {
+ free(s);
+ free(chr);
+ return NULL;
+ }
+ qemu_chr_reset(chr);
+ return chr;
+ }
+
+ static int win_chr_pipe_poll(void *opaque)
+ {
+ CharDriverState *chr = opaque;
+ WinCharState *s = chr->opaque;
+ DWORD size;
+
+ PeekNamedPipe(s->hcom, NULL, 0, NULL, &size, NULL);
+ if (size > 0) {
+ s->len = size;
+ win_chr_read_poll(chr);
+ win_chr_read(chr);
+ return 1;
+ }
+ return 0;
+ }
+
+ static int win_chr_pipe_init(CharDriverState *chr, const char *filename)
+ {
+ WinCharState *s = chr->opaque;
+ OVERLAPPED ov;
+ int ret;
+ DWORD size;
+ char openname[256];
+
+ s->fpipe = TRUE;
+
+ s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!s->hsend) {
+ fprintf(stderr, "Failed CreateEvent\n");
+ goto fail;
+ }
+ s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!s->hrecv) {
+ fprintf(stderr, "Failed CreateEvent\n");
+ goto fail;
+ }
+
+ snprintf(openname, sizeof(openname), "\\\\.\\pipe\\%s", filename);
+ s->hcom = CreateNamedPipe(openname, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE |
+ PIPE_WAIT,
+ MAXCONNECT, NSENDBUF, NRECVBUF, NTIMEOUT, NULL);
+ if (s->hcom == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "Failed CreateNamedPipe (%lu)\n", GetLastError());
+ s->hcom = NULL;
+ goto fail;
+ }
+
+ ZeroMemory(&ov, sizeof(ov));
+ ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ ret = ConnectNamedPipe(s->hcom, &ov);
+ if (ret) {
+ fprintf(stderr, "Failed ConnectNamedPipe\n");
+ goto fail;
+ }
+
+ ret = GetOverlappedResult(s->hcom, &ov, &size, TRUE);
+ if (!ret) {
+ fprintf(stderr, "Failed GetOverlappedResult\n");
+ if (ov.hEvent) {
+ CloseHandle(ov.hEvent);
+ ov.hEvent = NULL;
+ }
+ goto fail;
+ }
+
+ if (ov.hEvent) {
+ CloseHandle(ov.hEvent);
+ ov.hEvent = NULL;
+ }
+ qemu_add_polling_cb(win_chr_pipe_poll, chr);
+ return 0;
+
+ fail:
+ win_chr_close(chr);
+ return -1;
+ }
+
+
+ static CharDriverState *qemu_chr_open_win_pipe(const char *filename)
+ {
+ CharDriverState *chr;
+ WinCharState *s;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr)
+ return NULL;
+ s = qemu_mallocz(sizeof(WinCharState));
+ if (!s) {
+ free(chr);
+ return NULL;
+ }
+ chr->opaque = s;
+ chr->chr_write = win_chr_write;
+ chr->chr_close = win_chr_close;
+
+ if (win_chr_pipe_init(chr, filename) < 0) {
+ free(s);
+ free(chr);
+ return NULL;
+ }
+ qemu_chr_reset(chr);
+ return chr;
+ }
+
+ static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out)
+ {
+ CharDriverState *chr;
+ WinCharState *s;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr)
+ return NULL;
+ s = qemu_mallocz(sizeof(WinCharState));
+ if (!s) {
+ free(chr);
+ return NULL;
+ }
+ s->hcom = fd_out;
+ chr->opaque = s;
+ chr->chr_write = win_chr_write;
+ qemu_chr_reset(chr);
+ return chr;
+ }
+
+ static CharDriverState *qemu_chr_open_win_con(const char *filename)
+ {
+ return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE));
+ }
+
+ static CharDriverState *qemu_chr_open_win_file_out(const char *file_out)
+ {
+ HANDLE fd_out;
+
+ fd_out = CreateFile(file_out, GENERIC_WRITE, FILE_SHARE_READ, NULL,
+ OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (fd_out == INVALID_HANDLE_VALUE)
+ return NULL;
+
+ return qemu_chr_open_win_file(fd_out);
+ }
+ #endif /* !_WIN32 */
+
+ /***********************************************************/
+ /* UDP Net console */
+
+ typedef struct {
+ int fd;
+ struct sockaddr_in daddr;
+ uint8_t buf[1024];
+ int bufcnt;
+ int bufptr;
+ int max_size;
+ } NetCharDriver;
+
+ static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+ {
+ NetCharDriver *s = chr->opaque;
+
+ return sendto(s->fd, buf, len, 0,
+ (struct sockaddr *)&s->daddr, sizeof(struct sockaddr_in));
+ }
+
+ static int udp_chr_read_poll(void *opaque)
+ {
+ CharDriverState *chr = opaque;
+ NetCharDriver *s = chr->opaque;
+
+ s->max_size = qemu_chr_can_read(chr);
+
+ /* If there were any stray characters in the queue process them
+ * first
+ */
+ while (s->max_size > 0 && s->bufptr < s->bufcnt) {
+ qemu_chr_read(chr, &s->buf[s->bufptr], 1);
+ s->bufptr++;
+ s->max_size = qemu_chr_can_read(chr);
+ }
+ return s->max_size;
+ }
+
+ static void udp_chr_read(void *opaque)
+ {
+ CharDriverState *chr = opaque;
+ NetCharDriver *s = chr->opaque;
+
+ if (s->max_size == 0)
+ return;
+ s->bufcnt = recv(s->fd, s->buf, sizeof(s->buf), 0);
+ s->bufptr = s->bufcnt;
+ if (s->bufcnt <= 0)
+ return;
+
+ s->bufptr = 0;
+ while (s->max_size > 0 && s->bufptr < s->bufcnt) {
+ qemu_chr_read(chr, &s->buf[s->bufptr], 1);
+ s->bufptr++;
+ s->max_size = qemu_chr_can_read(chr);
+ }
+ }
+
+ static void udp_chr_update_read_handler(CharDriverState *chr)
+ {
+ NetCharDriver *s = chr->opaque;
+
+ if (s->fd >= 0) {
+ qemu_set_fd_handler2(s->fd, udp_chr_read_poll,
+ udp_chr_read, NULL, chr);
+ }
+ }
+
+ static CharDriverState *qemu_chr_open_udp(const char *def)
+ {
+ CharDriverState *chr = NULL;
+ NetCharDriver *s = NULL;
+ int fd = -1;
+ struct sockaddr_in saddr;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr)
+ goto return_err;
+ s = qemu_mallocz(sizeof(NetCharDriver));
+ if (!s)
+ goto return_err;
+
+ fd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ perror("socket(PF_INET, SOCK_DGRAM)");
+ goto return_err;
+ }
+
+ if (parse_host_src_port(&s->daddr, &saddr, def) < 0) {
+ printf("Could not parse: %s\n", def);
+ goto return_err;
+ }
+
+ if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
+ {
+ perror("bind");
+ goto return_err;
+ }
+
+ s->fd = fd;
+ s->bufcnt = 0;
+ s->bufptr = 0;
+ chr->opaque = s;
+ chr->chr_write = udp_chr_write;
+ chr->chr_update_read_handler = udp_chr_update_read_handler;
+ return chr;
+
+ return_err:
+ if (chr)
+ free(chr);
+ if (s)
+ free(s);
+ if (fd >= 0)
+ closesocket(fd);
+ return NULL;
+ }
+
+ /***********************************************************/
+ /* TCP Net console */
+
+ typedef struct {
+ int fd, listen_fd;
+ int connected;
+ int max_size;
+ int do_telnetopt;
+ int do_nodelay;
+ int is_unix;
+ } TCPCharDriver;
+
+ static void tcp_chr_accept(void *opaque);
+
+ static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+ {
+ TCPCharDriver *s = chr->opaque;
+ if (s->connected) {
+ return send_all(s->fd, buf, len);
+ } else {
+ /* XXX: indicate an error ? */
+ return len;
+ }
+ }
+
+ static int tcp_chr_read_poll(void *opaque)
+ {
+ CharDriverState *chr = opaque;
+ TCPCharDriver *s = chr->opaque;
+ if (!s->connected)
+ return 0;
+ s->max_size = qemu_chr_can_read(chr);
+ return s->max_size;
+ }
+
+ #define IAC 255
+ #define IAC_BREAK 243
+ static void tcp_chr_process_IAC_bytes(CharDriverState *chr,
+ TCPCharDriver *s,
+ uint8_t *buf, int *size)
+ {
+ /* Handle any telnet client's basic IAC options to satisfy char by
+ * char mode with no echo. All IAC options will be removed from
+ * the buf and the do_telnetopt variable will be used to track the
+ * state of the width of the IAC information.
+ *
+ * IAC commands come in sets of 3 bytes with the exception of the
+ * "IAC BREAK" command and the double IAC.
+ */
+
+ int i;
+ int j = 0;
+
+ for (i = 0; i < *size; i++) {
+ if (s->do_telnetopt > 1) {
+ if ((unsigned char)buf[i] == IAC && s->do_telnetopt == 2) {
+ /* Double IAC means send an IAC */
+ if (j != i)
+ buf[j] = buf[i];
+ j++;
+ s->do_telnetopt = 1;
+ } else {
+ if ((unsigned char)buf[i] == IAC_BREAK && s->do_telnetopt == 2) {
+ /* Handle IAC break commands by sending a serial break */
+ qemu_chr_event(chr, CHR_EVENT_BREAK);
+ s->do_telnetopt++;
+ }
+ s->do_telnetopt++;
+ }
+ if (s->do_telnetopt >= 4) {
+ s->do_telnetopt = 1;
+ }
+ } else {
+ if ((unsigned char)buf[i] == IAC) {
+ s->do_telnetopt = 2;
+ } else {
+ if (j != i)
+ buf[j] = buf[i];
+ j++;
+ }
+ }
+ }
+ *size = j;
+ }
+
+ static void tcp_chr_read(void *opaque)
+ {
+ CharDriverState *chr = opaque;
+ TCPCharDriver *s = chr->opaque;
+ uint8_t buf[1024];
+ int len, size;
+
+ if (!s->connected || s->max_size <= 0)
+ return;
+ len = sizeof(buf);
+ if (len > s->max_size)
+ len = s->max_size;
+ size = recv(s->fd, buf, len, 0);
+ if (size == 0) {
+ /* connection closed */
+ s->connected = 0;
+ if (s->listen_fd >= 0) {
+ qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr);
+ }
+ qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+ closesocket(s->fd);
+ s->fd = -1;
+ } else if (size > 0) {
+ if (s->do_telnetopt)
+ tcp_chr_process_IAC_bytes(chr, s, buf, &size);
+ if (size > 0)
+ qemu_chr_read(chr, buf, size);
+ }
+ }
+
+ static void tcp_chr_connect(void *opaque)
+ {
+ CharDriverState *chr = opaque;
+ TCPCharDriver *s = chr->opaque;
+
+ s->connected = 1;
+ qemu_set_fd_handler2(s->fd, tcp_chr_read_poll,
+ tcp_chr_read, NULL, chr);
+ qemu_chr_reset(chr);
+ }
+
+ #define IACSET(x,a,b,c) x[0] = a; x[1] = b; x[2] = c;
+ static void tcp_chr_telnet_init(int fd)
+ {
+ char buf[3];
+ /* Send the telnet negotion to put telnet in binary, no echo, single char mode */
+ IACSET(buf, 0xff, 0xfb, 0x01); /* IAC WILL ECHO */
+ send(fd, (char *)buf, 3, 0);
+ IACSET(buf, 0xff, 0xfb, 0x03); /* IAC WILL Suppress go ahead */
+ send(fd, (char *)buf, 3, 0);
+ IACSET(buf, 0xff, 0xfb, 0x00); /* IAC WILL Binary */
+ send(fd, (char *)buf, 3, 0);
+ IACSET(buf, 0xff, 0xfd, 0x00); /* IAC DO Binary */
+ send(fd, (char *)buf, 3, 0);
+ }
+
+ static void socket_set_nodelay(int fd)
+ {
+ int val = 1;
+ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
+ }
+
+ static void tcp_chr_accept(void *opaque)
+ {
+ CharDriverState *chr = opaque;
+ TCPCharDriver *s = chr->opaque;
+ struct sockaddr_in saddr;
-#ifndef _WIN32
++#ifndef NO_UNIX_SOCKETS
+ struct sockaddr_un uaddr;
+ #endif
+ struct sockaddr *addr;
+ socklen_t len;
+ int fd;
+
+ for(;;) {
++#ifndef NO_UNIX_SOCKETS
+ if (s->is_unix) {
+ len = sizeof(uaddr);
+ addr = (struct sockaddr *)&uaddr;
+ } else
+ #endif
+ {
+ len = sizeof(saddr);
+ addr = (struct sockaddr *)&saddr;
+ }
+ fd = accept(s->listen_fd, addr, &len);
+ if (fd < 0 && errno != EINTR) {
+ return;
+ } else if (fd >= 0) {
+ if (s->do_telnetopt)
+ tcp_chr_telnet_init(fd);
+ break;
+ }
+ }
+ socket_set_nonblock(fd);
+ if (s->do_nodelay)
+ socket_set_nodelay(fd);
+ s->fd = fd;
+ qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL);
+ tcp_chr_connect(chr);
+ }
+
+ static void tcp_chr_close(CharDriverState *chr)
+ {
+ TCPCharDriver *s = chr->opaque;
+ if (s->fd >= 0)
+ closesocket(s->fd);
+ if (s->listen_fd >= 0)
+ closesocket(s->listen_fd);
+ qemu_free(s);
+ }
+
+ static CharDriverState *qemu_chr_open_tcp(const char *host_str,
+ int is_telnet,
+ int is_unix)
+ {
+ CharDriverState *chr = NULL;
+ TCPCharDriver *s = NULL;
+ int fd = -1, offset = 0;
+ int is_listen = 0;
+ int is_waitconnect = 1;
+ int do_nodelay = 0;
+ const char *ptr;
+
+ ptr = host_str;
+ while((ptr = strchr(ptr,','))) {
+ ptr++;
+ if (!strncmp(ptr,"server",6)) {
+ is_listen = 1;
+ } else if (!strncmp(ptr,"nowait",6)) {
+ is_waitconnect = 0;
+ } else if (!strncmp(ptr,"nodelay",6)) {
+ do_nodelay = 1;
+ } else if (!strncmp(ptr,"to=",3)) {
+ /* nothing, inet_listen() parses this one */;
+ } else {
+ printf("Unknown option: %s\n", ptr);
+ goto fail;
+ }
+ }
+ if (!is_listen)
+ is_waitconnect = 0;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr)
+ goto fail;
+ s = qemu_mallocz(sizeof(TCPCharDriver));
+ if (!s)
+ goto fail;
+
+ if (is_listen) {
+ chr->filename = qemu_malloc(256);
+ if (is_unix) {
+ pstrcpy(chr->filename, 256, "unix:");
+ } else if (is_telnet) {
+ pstrcpy(chr->filename, 256, "telnet:");
+ } else {
+ pstrcpy(chr->filename, 256, "tcp:");
+ }
+ offset = strlen(chr->filename);
+ }
+ if (is_unix) {
+ if (is_listen) {
+ fd = unix_listen(host_str, chr->filename + offset, 256 - offset);
+ } else {
+ fd = unix_connect(host_str);
+ }
+ } else {
+ if (is_listen) {
+ fd = inet_listen(host_str, chr->filename + offset, 256 - offset,
+ SOCK_STREAM, 0);
+ } else {
+ fd = inet_connect(host_str, SOCK_STREAM);
+ }
+ }
+ if (fd < 0)
+ goto fail;
+
+ if (!is_waitconnect)
+ socket_set_nonblock(fd);
+
+ s->connected = 0;
+ s->fd = -1;
+ s->listen_fd = -1;
+ s->is_unix = is_unix;
+ s->do_nodelay = do_nodelay && !is_unix;
+
+ chr->opaque = s;
+ chr->chr_write = tcp_chr_write;
+ chr->chr_close = tcp_chr_close;
+
+ if (is_listen) {
+ s->listen_fd = fd;
+ qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr);
+ if (is_telnet)
+ s->do_telnetopt = 1;
+ } else {
+ s->connected = 1;
+ s->fd = fd;
+ socket_set_nodelay(fd);
+ tcp_chr_connect(chr);
+ }
+
+ if (is_listen && is_waitconnect) {
+ printf("QEMU waiting for connection on: %s\n",
+ chr->filename ? chr->filename : host_str);
+ tcp_chr_accept(chr);
+ socket_set_nonblock(s->listen_fd);
+ }
+
+ return chr;
+ fail:
+ if (fd >= 0)
+ closesocket(fd);
+ qemu_free(s);
+ qemu_free(chr);
+ return NULL;
+ }
+
+ static TAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs
+ = TAILQ_HEAD_INITIALIZER(chardevs);
+
+ CharDriverState *qemu_chr_open(const char *label, const char *filename)
+ {
+ const char *p;
+ CharDriverState *chr;
+
+ if (!strcmp(filename, "vc")) {
+ chr = text_console_init(&display_state, 0);
+ } else
+ if (strstart(filename, "vc:", &p)) {
+ chr = text_console_init(&display_state, p);
+ } else
+ if (!strcmp(filename, "null")) {
+ chr = qemu_chr_open_null();
+ } else
+ if (strstart(filename, "tcp:", &p)) {
+ chr = qemu_chr_open_tcp(p, 0, 0);
+ } else
+ if (strstart(filename, "telnet:", &p)) {
+ chr = qemu_chr_open_tcp(p, 1, 0);
+ } else
+ if (strstart(filename, "udp:", &p)) {
+ chr = qemu_chr_open_udp(p);
+ } else
+ if (strstart(filename, "mon:", &p)) {
+ chr = qemu_chr_open(label, p);
+ if (chr) {
+ chr = qemu_chr_open_mux(chr);
+ monitor_init(chr, !nographic);
+ } else {
+ printf("Unable to open driver: %s\n", p);
+ }
+ } else
+ #ifndef _WIN32
+ if (strstart(filename, "unix:", &p)) {
+ chr = qemu_chr_open_tcp(p, 0, 1);
+ } else if (strstart(filename, "file:", &p)) {
+ chr = qemu_chr_open_file_out(p);
+ } else if (strstart(filename, "pipe:", &p)) {
+ chr = qemu_chr_open_pipe(p);
+ } else if (!strcmp(filename, "pty")) {
+ chr = qemu_chr_open_pty();
+ } else if (!strcmp(filename, "stdio")) {
+ chr = qemu_chr_open_stdio();
+ } else
+ #if defined(__linux__)
+ if (strstart(filename, "/dev/parport", NULL)) {
+ chr = qemu_chr_open_pp(filename);
+ } else
+ #elif defined(__FreeBSD__)
+ if (strstart(filename, "/dev/ppi", NULL)) {
+ chr = qemu_chr_open_pp(filename);
+ } else
+ #endif
+ #if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
+ || defined(__NetBSD__) || defined(__OpenBSD__)
+ if (strstart(filename, "/dev/", NULL)) {
+ chr = qemu_chr_open_tty(filename);
+ } else
+ #endif
+ #else /* !_WIN32 */
+ if (strstart(filename, "COM", NULL)) {
+ chr = qemu_chr_open_win(filename);
+ } else
+ if (strstart(filename, "pipe:", &p)) {
+ chr = qemu_chr_open_win_pipe(p);
+ } else
+ if (strstart(filename, "con:", NULL)) {
+ chr = qemu_chr_open_win_con(filename);
+ } else
+ if (strstart(filename, "file:", &p)) {
+ chr = qemu_chr_open_win_file_out(p);
+ } else
+ #endif
+ #ifdef CONFIG_BRLAPI
+ if (!strcmp(filename, "braille")) {
+ chr = chr_baum_init();
+ } else
+ #endif
+ {
+ chr = NULL;
+ }
+
+ if (chr) {
+ if (!chr->filename)
+ chr->filename = qemu_strdup(filename);
+ chr->label = qemu_strdup(label);
+ TAILQ_INSERT_TAIL(&chardevs, chr, next);
+ }
+ return chr;
+ }
+
+ void qemu_chr_close(CharDriverState *chr)
+ {
+ TAILQ_REMOVE(&chardevs, chr, next);
+ if (chr->chr_close)
+ chr->chr_close(chr);
+ qemu_free(chr->filename);
+ qemu_free(chr->label);
+ qemu_free(chr);
+ }
+
+ void qemu_chr_info(void)
+ {
+ CharDriverState *chr;
+
+ TAILQ_FOREACH(chr, &chardevs, next) {
+ term_printf("%s: filename=%s\n", chr->label, chr->filename);
+ }
+ }
--- /dev/null
+ /*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 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 "qemu-common.h"
+ #include "hw/hw.h"
+ #include "net.h"
+ #include "console.h"
+ #include "sysemu.h"
+ #include "qemu-timer.h"
+ #include "qemu-char.h"
+ #include "block.h"
+ #include "audio/audio.h"
+ #include "migration.h"
+ #include "qemu_socket.h"
+
+ #include <unistd.h>
+ #include <fcntl.h>
+ #include <signal.h>
+ #include <time.h>
+ #include <errno.h>
+ #include <sys/time.h>
+ #include <zlib.h>
+
+ #ifndef _WIN32
+ #include <sys/times.h>
+ #include <sys/wait.h>
+ #include <termios.h>
+ #include <sys/mman.h>
+ #include <sys/ioctl.h>
+ #include <sys/resource.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <net/if.h>
+ #if defined(__NetBSD__)
+ #include <net/if_tap.h>
+ #endif
+ #ifdef __linux__
+ #include <linux/if_tun.h>
+ #endif
+ #include <arpa/inet.h>
+ #include <dirent.h>
+ #include <netdb.h>
+ #include <sys/select.h>
+ #ifdef _BSD
+ #include <sys/stat.h>
+ #ifdef __FreeBSD__
+ #include <libutil.h>
+ #else
+ #include <util.h>
+ #endif
+ #elif defined (__GLIBC__) && defined (__FreeBSD_kernel__)
+ #include <freebsd/stdlib.h>
+ #else
+ #ifdef __linux__
+ #include <pty.h>
+ #include <malloc.h>
+ #include <linux/rtc.h>
+ #endif
+ #endif
+ #endif
+
+ #ifdef _WIN32
+ #include <malloc.h>
+ #include <sys/timeb.h>
+ #include <mmsystem.h>
+ #define getopt_long_only getopt_long
+ #define memalign(align, size) malloc(size)
+ #endif
+
+ /* point to the block driver where the snapshots are managed */
+ static BlockDriverState *bs_snapshots;
+
+ #define SELF_ANNOUNCE_ROUNDS 5
+ #define ETH_P_EXPERIMENTAL 0x01F1 /* just a number */
+ //#define ETH_P_EXPERIMENTAL 0x0012 /* make it the size of the packet */
+ #define EXPERIMENTAL_MAGIC 0xf1f23f4f
+
+ static int announce_self_create(uint8_t *buf,
+ uint8_t *mac_addr)
+ {
+ uint32_t magic = EXPERIMENTAL_MAGIC;
+ uint16_t proto = htons(ETH_P_EXPERIMENTAL);
+
+ /* FIXME: should we send a different packet (arp/rarp/ping)? */
+
+ memset(buf, 0xff, 6); /* h_dst */
+ memcpy(buf + 6, mac_addr, 6); /* h_src */
+ memcpy(buf + 12, &proto, 2); /* h_proto */
+ memcpy(buf + 14, &magic, 4); /* magic */
+
+ return 18; /* len */
+ }
+
+ void qemu_announce_self(void)
+ {
+ int i, j, len;
+ VLANState *vlan;
+ VLANClientState *vc;
+ uint8_t buf[256];
+
+ for (i = 0; i < nb_nics; i++) {
+ len = announce_self_create(buf, nd_table[i].macaddr);
+ vlan = nd_table[i].vlan;
+ for(vc = vlan->first_client; vc != NULL; vc = vc->next) {
+ for (j=0; j < SELF_ANNOUNCE_ROUNDS; j++)
+ vc->fd_read(vc->opaque, buf, len);
+ }
+ }
+ }
+
+ /***********************************************************/
+ /* savevm/loadvm support */
+
+ #define IO_BUF_SIZE 32768
+
+ struct QEMUFile {
+ QEMUFilePutBufferFunc *put_buffer;
+ QEMUFileGetBufferFunc *get_buffer;
+ QEMUFileCloseFunc *close;
+ QEMUFileRateLimit *rate_limit;
+ void *opaque;
+ int is_write;
+
+ int64_t buf_offset; /* start of buffer when writing, end of buffer
+ when reading */
+ int buf_index;
+ int buf_size; /* 0 when writing */
+ uint8_t buf[IO_BUF_SIZE];
+
+ int has_error;
+ };
+
+ typedef struct QEMUFilePopen
+ {
+ FILE *popen_file;
+ QEMUFile *file;
+ } QEMUFilePopen;
+
+ typedef struct QEMUFileSocket
+ {
+ int fd;
+ QEMUFile *file;
+ } QEMUFileSocket;
+
+ static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+ {
+ QEMUFileSocket *s = opaque;
+ ssize_t len;
+
+ do {
+ len = recv(s->fd, buf, size, 0);
+ } while (len == -1 && socket_error() == EINTR);
+
+ if (len == -1)
+ len = -socket_error();
+
+ return len;
+ }
+
+ static int socket_close(void *opaque)
+ {
+ QEMUFileSocket *s = opaque;
+ qemu_free(s);
+ return 0;
+ }
+
+ static int popen_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
+ {
+ QEMUFilePopen *s = opaque;
+ return fwrite(buf, 1, size, s->popen_file);
+ }
+
+ static int popen_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+ {
+ QEMUFilePopen *s = opaque;
+ return fread(buf, 1, size, s->popen_file);
+ }
+
+ static int popen_close(void *opaque)
+ {
+ QEMUFilePopen *s = opaque;
+ pclose(s->popen_file);
+ qemu_free(s);
+ return 0;
+ }
+
+ QEMUFile *qemu_popen(FILE *popen_file, const char *mode)
+ {
+ QEMUFilePopen *s;
+
+ if (popen_file == NULL || mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) {
+ fprintf(stderr, "qemu_popen: Argument validity check failed\n");
+ return NULL;
+ }
+
+ s = qemu_mallocz(sizeof(QEMUFilePopen));
+ if (!s) {
+ fprintf(stderr, "qemu_popen: malloc failed\n");
+ return NULL;
+ }
+
+ s->popen_file = popen_file;
+
+ if(mode[0] == 'r') {
+ s->file = qemu_fopen_ops(s, NULL, popen_get_buffer, popen_close, NULL);
+ } else {
+ s->file = qemu_fopen_ops(s, popen_put_buffer, NULL, popen_close, NULL);
+ }
+ fprintf(stderr, "qemu_popen: returning result of qemu_fopen_ops\n");
+ return s->file;
+ }
+
+ QEMUFile *qemu_popen_cmd(const char *command, const char *mode)
+ {
+ FILE *popen_file;
+
+ popen_file = popen(command, mode);
+ if(popen_file == NULL) {
+ return NULL;
+ }
+
+ return qemu_popen(popen_file, mode);
+ }
+
+ QEMUFile *qemu_fopen_socket(int fd)
+ {
+ QEMUFileSocket *s = qemu_mallocz(sizeof(QEMUFileSocket));
+
+ if (s == NULL)
+ return NULL;
+
+ s->fd = fd;
+ s->file = qemu_fopen_ops(s, NULL, socket_get_buffer, socket_close, NULL);
+ return s->file;
+ }
+
+ typedef struct QEMUFileStdio
+ {
+ FILE *outfile;
+ } QEMUFileStdio;
+
+ static int file_put_buffer(void *opaque, const uint8_t *buf,
+ int64_t pos, int size)
+ {
+ QEMUFileStdio *s = opaque;
+ fseek(s->outfile, pos, SEEK_SET);
+ fwrite(buf, 1, size, s->outfile);
+ return size;
+ }
+
+ static int file_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+ {
+ QEMUFileStdio *s = opaque;
+ fseek(s->outfile, pos, SEEK_SET);
+ return fread(buf, 1, size, s->outfile);
+ }
+
+ static int file_close(void *opaque)
+ {
+ QEMUFileStdio *s = opaque;
+ fclose(s->outfile);
+ qemu_free(s);
+ return 0;
+ }
+
+ QEMUFile *qemu_fopen(const char *filename, const char *mode)
+ {
+ QEMUFileStdio *s;
+
+ s = qemu_mallocz(sizeof(QEMUFileStdio));
+ if (!s)
+ return NULL;
+
+ s->outfile = fopen(filename, mode);
+ if (!s->outfile)
+ goto fail;
+
+ if (!strcmp(mode, "wb"))
+ return qemu_fopen_ops(s, file_put_buffer, NULL, file_close, NULL);
+ else if (!strcmp(mode, "rb"))
+ return qemu_fopen_ops(s, NULL, file_get_buffer, file_close, NULL);
+
+ fail:
+ if (s->outfile)
+ fclose(s->outfile);
+ qemu_free(s);
+ return NULL;
+ }
+
+ typedef struct QEMUFileBdrv
+ {
+ BlockDriverState *bs;
+ int64_t base_offset;
+ } QEMUFileBdrv;
+
+ static int bdrv_put_buffer(void *opaque, const uint8_t *buf,
+ int64_t pos, int size)
+ {
+ QEMUFileBdrv *s = opaque;
+ bdrv_pwrite(s->bs, s->base_offset + pos, buf, size);
+ return size;
+ }
+
+ static int bdrv_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+ {
+ QEMUFileBdrv *s = opaque;
+ return bdrv_pread(s->bs, s->base_offset + pos, buf, size);
+ }
+
+ static int bdrv_fclose(void *opaque)
+ {
+ QEMUFileBdrv *s = opaque;
+ qemu_free(s);
+ return 0;
+ }
+
+ static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int64_t offset, int is_writable)
+ {
+ QEMUFileBdrv *s;
+
+ s = qemu_mallocz(sizeof(QEMUFileBdrv));
+ if (!s)
+ return NULL;
+
+ s->bs = bs;
+ s->base_offset = offset;
+
+ if (is_writable)
+ return qemu_fopen_ops(s, bdrv_put_buffer, NULL, bdrv_fclose, NULL);
+
+ return qemu_fopen_ops(s, NULL, bdrv_get_buffer, bdrv_fclose, NULL);
+ }
+
+ QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer,
+ QEMUFileGetBufferFunc *get_buffer,
+ QEMUFileCloseFunc *close,
+ QEMUFileRateLimit *rate_limit)
+ {
+ QEMUFile *f;
+
+ f = qemu_mallocz(sizeof(QEMUFile));
+ if (!f)
+ return NULL;
+
+ f->opaque = opaque;
+ f->put_buffer = put_buffer;
+ f->get_buffer = get_buffer;
+ f->close = close;
+ f->rate_limit = rate_limit;
+ f->is_write = 0;
+
+ return f;
+ }
+
+ int qemu_file_has_error(QEMUFile *f)
+ {
+ return f->has_error;
+ }
+
+ void qemu_fflush(QEMUFile *f)
+ {
+ if (!f->put_buffer)
+ return;
+
+ if (f->is_write && f->buf_index > 0) {
+ int len;
+
+ len = f->put_buffer(f->opaque, f->buf, f->buf_offset, f->buf_index);
+ if (len > 0)
+ f->buf_offset += f->buf_index;
+ else
+ f->has_error = 1;
+ f->buf_index = 0;
+ }
+ }
+
+ static void qemu_fill_buffer(QEMUFile *f)
+ {
+ int len;
+
+ if (!f->get_buffer)
+ return;
+
+ if (f->is_write)
+ abort();
+
+ len = f->get_buffer(f->opaque, f->buf, f->buf_offset, IO_BUF_SIZE);
+ if (len > 0) {
+ f->buf_index = 0;
+ f->buf_size = len;
+ f->buf_offset += len;
+ } else if (len != -EAGAIN)
+ f->has_error = 1;
+ }
+
+ int qemu_fclose(QEMUFile *f)
+ {
+ int ret = 0;
+ qemu_fflush(f);
+ if (f->close)
+ ret = f->close(f->opaque);
+ qemu_free(f);
+ return ret;
+ }
+
+ void qemu_file_put_notify(QEMUFile *f)
+ {
+ f->put_buffer(f->opaque, NULL, 0, 0);
+ }
+
+ void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
+ {
+ int l;
+
+ if (!f->has_error && f->is_write == 0 && f->buf_index > 0) {
+ fprintf(stderr,
+ "Attempted to write to buffer while read buffer is not empty\n");
+ abort();
+ }
+
+ while (!f->has_error && size > 0) {
+ l = IO_BUF_SIZE - f->buf_index;
+ if (l > size)
+ l = size;
+ memcpy(f->buf + f->buf_index, buf, l);
+ f->is_write = 1;
+ f->buf_index += l;
+ buf += l;
+ size -= l;
+ if (f->buf_index >= IO_BUF_SIZE)
+ qemu_fflush(f);
+ }
+ }
+
+ void qemu_put_byte(QEMUFile *f, int v)
+ {
+ if (!f->has_error && f->is_write == 0 && f->buf_index > 0) {
+ fprintf(stderr,
+ "Attempted to write to buffer while read buffer is not empty\n");
+ abort();
+ }
+
+ f->buf[f->buf_index++] = v;
+ f->is_write = 1;
+ if (f->buf_index >= IO_BUF_SIZE)
+ qemu_fflush(f);
+ }
+
+ int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size1)
+ {
+ int size, l;
+
+ if (f->is_write)
+ abort();
+
+ size = size1;
+ while (size > 0) {
+ l = f->buf_size - f->buf_index;
+ if (l == 0) {
+ qemu_fill_buffer(f);
+ l = f->buf_size - f->buf_index;
+ if (l == 0)
+ break;
+ }
+ if (l > size)
+ l = size;
+ memcpy(buf, f->buf + f->buf_index, l);
+ f->buf_index += l;
+ buf += l;
+ size -= l;
+ }
+ return size1 - size;
+ }
+
+ int qemu_get_byte(QEMUFile *f)
+ {
+ if (f->is_write)
+ abort();
+
+ if (f->buf_index >= f->buf_size) {
+ qemu_fill_buffer(f);
+ if (f->buf_index >= f->buf_size)
+ return 0;
+ }
+ return f->buf[f->buf_index++];
+ }
+
+ int64_t qemu_ftell(QEMUFile *f)
+ {
+ return f->buf_offset - f->buf_size + f->buf_index;
+ }
+
+ int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence)
+ {
+ if (whence == SEEK_SET) {
+ /* nothing to do */
+ } else if (whence == SEEK_CUR) {
+ pos += qemu_ftell(f);
+ } else {
+ /* SEEK_END not supported */
+ return -1;
+ }
+ if (f->put_buffer) {
+ qemu_fflush(f);
+ f->buf_offset = pos;
+ } else {
+ f->buf_offset = pos;
+ f->buf_index = 0;
+ f->buf_size = 0;
+ }
+ return pos;
+ }
+
+ int qemu_file_rate_limit(QEMUFile *f)
+ {
+ if (f->rate_limit)
+ return f->rate_limit(f->opaque);
+
+ return 0;
+ }
+
+ void qemu_put_be16(QEMUFile *f, unsigned int v)
+ {
+ qemu_put_byte(f, v >> 8);
+ qemu_put_byte(f, v);
+ }
+
+ void qemu_put_be32(QEMUFile *f, unsigned int v)
+ {
+ qemu_put_byte(f, v >> 24);
+ qemu_put_byte(f, v >> 16);
+ qemu_put_byte(f, v >> 8);
+ qemu_put_byte(f, v);
+ }
+
+ void qemu_put_be64(QEMUFile *f, uint64_t v)
+ {
+ qemu_put_be32(f, v >> 32);
+ qemu_put_be32(f, v);
+ }
+
+ unsigned int qemu_get_be16(QEMUFile *f)
+ {
+ unsigned int v;
+ v = qemu_get_byte(f) << 8;
+ v |= qemu_get_byte(f);
+ return v;
+ }
+
+ unsigned int qemu_get_be32(QEMUFile *f)
+ {
+ unsigned int v;
+ v = qemu_get_byte(f) << 24;
+ v |= qemu_get_byte(f) << 16;
+ v |= qemu_get_byte(f) << 8;
+ v |= qemu_get_byte(f);
+ return v;
+ }
+
+ uint64_t qemu_get_be64(QEMUFile *f)
+ {
+ uint64_t v;
+ v = (uint64_t)qemu_get_be32(f) << 32;
+ v |= qemu_get_be32(f);
+ return v;
+ }
+
+ typedef struct SaveStateEntry {
+ char idstr[256];
+ int instance_id;
+ int version_id;
+ int section_id;
+ SaveLiveStateHandler *save_live_state;
+ SaveStateHandler *save_state;
+ LoadStateHandler *load_state;
+ void *opaque;
+ struct SaveStateEntry *next;
+ } SaveStateEntry;
+
+ static SaveStateEntry *first_se;
+
+ /* TODO: Individual devices generally have very little idea about the rest
+ of the system, so instance_id should be removed/replaced.
+ Meanwhile pass -1 as instance_id if you do not already have a clearly
+ distinguishing id for all instances of your device class. */
+ int register_savevm_live(const char *idstr,
+ int instance_id,
+ int version_id,
+ SaveLiveStateHandler *save_live_state,
+ SaveStateHandler *save_state,
+ LoadStateHandler *load_state,
+ void *opaque)
+ {
+ SaveStateEntry *se, **pse;
+ static int global_section_id;
+
+ se = qemu_malloc(sizeof(SaveStateEntry));
+ if (!se)
+ return -1;
+ pstrcpy(se->idstr, sizeof(se->idstr), idstr);
+ se->instance_id = (instance_id == -1) ? 0 : instance_id;
+ se->version_id = version_id;
+ se->section_id = global_section_id++;
+ se->save_live_state = save_live_state;
+ se->save_state = save_state;
+ se->load_state = load_state;
+ se->opaque = opaque;
+ se->next = NULL;
+
+ /* add at the end of list */
+ pse = &first_se;
+ while (*pse != NULL) {
+ if (instance_id == -1
+ && strcmp(se->idstr, (*pse)->idstr) == 0
+ && se->instance_id <= (*pse)->instance_id)
+ se->instance_id = (*pse)->instance_id + 1;
+ pse = &(*pse)->next;
+ }
+ *pse = se;
+ return 0;
+ }
+
+ int register_savevm(const char *idstr,
+ int instance_id,
+ int version_id,
+ SaveStateHandler *save_state,
+ LoadStateHandler *load_state,
+ void *opaque)
+ {
+ return register_savevm_live(idstr, instance_id, version_id,
+ NULL, save_state, load_state, opaque);
+ }
+
+ #define QEMU_VM_FILE_MAGIC 0x5145564d
+ #define QEMU_VM_FILE_VERSION_COMPAT 0x00000002
+ #define QEMU_VM_FILE_VERSION 0x00000003
+
+ #define QEMU_VM_EOF 0x00
+ #define QEMU_VM_SECTION_START 0x01
+ #define QEMU_VM_SECTION_PART 0x02
+ #define QEMU_VM_SECTION_END 0x03
+ #define QEMU_VM_SECTION_FULL 0x04
+
+ int qemu_savevm_state_begin(QEMUFile *f)
+ {
+ SaveStateEntry *se;
+
+ qemu_put_be32(f, QEMU_VM_FILE_MAGIC);
+ qemu_put_be32(f, QEMU_VM_FILE_VERSION);
+
+ for (se = first_se; se != NULL; se = se->next) {
+ int len;
+
+ if (se->save_live_state == NULL)
+ continue;
+
+ /* Section type */
+ qemu_put_byte(f, QEMU_VM_SECTION_START);
+ qemu_put_be32(f, se->section_id);
+
+ /* ID string */
+ len = strlen(se->idstr);
+ qemu_put_byte(f, len);
+ qemu_put_buffer(f, (uint8_t *)se->idstr, len);
+
+ qemu_put_be32(f, se->instance_id);
+ qemu_put_be32(f, se->version_id);
+
+ se->save_live_state(f, QEMU_VM_SECTION_START, se->opaque);
+ }
+
+ if (qemu_file_has_error(f))
+ return -EIO;
+
+ return 0;
+ }
+
+ int qemu_savevm_state_iterate(QEMUFile *f)
+ {
+ SaveStateEntry *se;
+ int ret = 1;
+
+ for (se = first_se; se != NULL; se = se->next) {
+ if (se->save_live_state == NULL)
+ continue;
+
+ /* Section type */
+ qemu_put_byte(f, QEMU_VM_SECTION_PART);
+ qemu_put_be32(f, se->section_id);
+
+ ret &= !!se->save_live_state(f, QEMU_VM_SECTION_PART, se->opaque);
+ }
+
+ if (ret)
+ return 1;
+
+ if (qemu_file_has_error(f))
+ return -EIO;
+
+ return 0;
+ }
+
+ int qemu_savevm_state_complete(QEMUFile *f)
+ {
+ SaveStateEntry *se;
+
+ for (se = first_se; se != NULL; se = se->next) {
+ if (se->save_live_state == NULL)
+ continue;
+
+ /* Section type */
+ qemu_put_byte(f, QEMU_VM_SECTION_END);
+ qemu_put_be32(f, se->section_id);
+
+ se->save_live_state(f, QEMU_VM_SECTION_END, se->opaque);
+ }
+
+ for(se = first_se; se != NULL; se = se->next) {
+ int len;
+
+ if (se->save_state == NULL)
+ continue;
+
+ /* Section type */
+ qemu_put_byte(f, QEMU_VM_SECTION_FULL);
+ qemu_put_be32(f, se->section_id);
+
+ /* ID string */
+ len = strlen(se->idstr);
+ qemu_put_byte(f, len);
+ qemu_put_buffer(f, (uint8_t *)se->idstr, len);
+
+ qemu_put_be32(f, se->instance_id);
+ qemu_put_be32(f, se->version_id);
+
+ se->save_state(f, se->opaque);
+ }
+
+ qemu_put_byte(f, QEMU_VM_EOF);
+
+ if (qemu_file_has_error(f))
+ return -EIO;
+
+ return 0;
+ }
+
+ int qemu_savevm_state(QEMUFile *f)
+ {
+ int saved_vm_running;
+ int ret;
+
+ saved_vm_running = vm_running;
+ vm_stop(0);
+
+ bdrv_flush_all();
+
+ ret = qemu_savevm_state_begin(f);
+ if (ret < 0)
+ goto out;
+
+ do {
+ ret = qemu_savevm_state_iterate(f);
+ if (ret < 0)
+ goto out;
+ } while (ret == 0);
+
+ ret = qemu_savevm_state_complete(f);
+
+ out:
+ if (qemu_file_has_error(f))
+ ret = -EIO;
+
+ if (!ret && saved_vm_running)
+ vm_start();
+
+ return ret;
+ }
+
+ static SaveStateEntry *find_se(const char *idstr, int instance_id)
+ {
+ SaveStateEntry *se;
+
+ for(se = first_se; se != NULL; se = se->next) {
+ if (!strcmp(se->idstr, idstr) &&
+ instance_id == se->instance_id)
+ return se;
+ }
+ return NULL;
+ }
+
+ typedef struct LoadStateEntry {
+ SaveStateEntry *se;
+ int section_id;
+ int version_id;
+ struct LoadStateEntry *next;
+ } LoadStateEntry;
+
+ static int qemu_loadvm_state_v2(QEMUFile *f)
+ {
+ SaveStateEntry *se;
+ int len, ret, instance_id, record_len, version_id;
+ int64_t total_len, end_pos, cur_pos;
+ char idstr[256];
+
+ total_len = qemu_get_be64(f);
+ end_pos = total_len + qemu_ftell(f);
+ for(;;) {
+ if (qemu_ftell(f) >= end_pos)
+ break;
+ len = qemu_get_byte(f);
+ qemu_get_buffer(f, (uint8_t *)idstr, len);
+ idstr[len] = '\0';
+ instance_id = qemu_get_be32(f);
+ version_id = qemu_get_be32(f);
+ record_len = qemu_get_be32(f);
+ cur_pos = qemu_ftell(f);
+ se = find_se(idstr, instance_id);
+ if (!se) {
+ fprintf(stderr, "qemu: warning: instance 0x%x of device '%s' not present in current VM\n",
+ instance_id, idstr);
+ } else {
+ ret = se->load_state(f, se->opaque, version_id);
+ if (ret < 0) {
+ fprintf(stderr, "qemu: warning: error while loading state for instance 0x%x of device '%s'\n",
+ instance_id, idstr);
+ }
+ }
+ /* always seek to exact end of record */
+ qemu_fseek(f, cur_pos + record_len, SEEK_SET);
+ }
+
+ if (qemu_file_has_error(f))
+ return -EIO;
+
+ return 0;
+ }
+
+ int qemu_loadvm_state(QEMUFile *f)
+ {
+ LoadStateEntry *first_le = NULL;
+ uint8_t section_type;
+ unsigned int v;
+ int ret;
+
+ v = qemu_get_be32(f);
+ if (v != QEMU_VM_FILE_MAGIC)
+ return -EINVAL;
+
+ v = qemu_get_be32(f);
+ if (v == QEMU_VM_FILE_VERSION_COMPAT)
+ return qemu_loadvm_state_v2(f);
+ if (v != QEMU_VM_FILE_VERSION)
+ return -ENOTSUP;
+
+ while ((section_type = qemu_get_byte(f)) != QEMU_VM_EOF) {
+ uint32_t instance_id, version_id, section_id;
+ LoadStateEntry *le;
+ SaveStateEntry *se;
+ char idstr[257];
+ int len;
+
+ switch (section_type) {
+ case QEMU_VM_SECTION_START:
+ case QEMU_VM_SECTION_FULL:
+ /* Read section start */
+ section_id = qemu_get_be32(f);
+ len = qemu_get_byte(f);
+ qemu_get_buffer(f, (uint8_t *)idstr, len);
+ idstr[len] = 0;
+ instance_id = qemu_get_be32(f);
+ version_id = qemu_get_be32(f);
+
+ /* Find savevm section */
+ se = find_se(idstr, instance_id);
+ if (se == NULL) {
+ fprintf(stderr, "Unknown savevm section or instance '%s' %d\n", idstr, instance_id);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Validate version */
+ if (version_id > se->version_id) {
+ fprintf(stderr, "savevm: unsupported version %d for '%s' v%d\n",
+ version_id, idstr, se->version_id);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Add entry */
+ le = qemu_mallocz(sizeof(*le));
+ if (le == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ le->se = se;
+ le->section_id = section_id;
+ le->version_id = version_id;
+ le->next = first_le;
+ first_le = le;
+
+ le->se->load_state(f, le->se->opaque, le->version_id);
+ break;
+ case QEMU_VM_SECTION_PART:
+ case QEMU_VM_SECTION_END:
+ section_id = qemu_get_be32(f);
+
+ for (le = first_le; le && le->section_id != section_id; le = le->next);
+ if (le == NULL) {
+ fprintf(stderr, "Unknown savevm section %d\n", section_id);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ le->se->load_state(f, le->se->opaque, le->version_id);
+ break;
+ default:
+ fprintf(stderr, "Unknown savevm section type %d\n", section_type);
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ ret = 0;
+
+ out:
+ while (first_le) {
+ LoadStateEntry *le = first_le;
+ first_le = first_le->next;
+ qemu_free(le);
+ }
+
+ if (qemu_file_has_error(f))
+ ret = -EIO;
+
+ return ret;
+ }
+
+ /* device can contain snapshots */
+ static int bdrv_can_snapshot(BlockDriverState *bs)
+ {
+ return (bs &&
+ !bdrv_is_removable(bs) &&
+ !bdrv_is_read_only(bs));
+ }
+
+ /* device must be snapshots in order to have a reliable snapshot */
+ static int bdrv_has_snapshot(BlockDriverState *bs)
+ {
+ return (bs &&
+ !bdrv_is_removable(bs) &&
+ !bdrv_is_read_only(bs));
+ }
+
+ static BlockDriverState *get_bs_snapshots(void)
+ {
+ BlockDriverState *bs;
+ int i;
+
+ if (bs_snapshots)
+ return bs_snapshots;
+ for(i = 0; i <= nb_drives; i++) {
+ bs = drives_table[i].bdrv;
+ if (bdrv_can_snapshot(bs))
+ goto ok;
+ }
+ return NULL;
+ ok:
+ bs_snapshots = bs;
+ return bs;
+ }
+
+ static int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
+ const char *name)
+ {
+ QEMUSnapshotInfo *sn_tab, *sn;
+ int nb_sns, i, ret;
+
+ ret = -ENOENT;
+ nb_sns = bdrv_snapshot_list(bs, &sn_tab);
+ if (nb_sns < 0)
+ return ret;
+ for(i = 0; i < nb_sns; i++) {
+ sn = &sn_tab[i];
+ if (!strcmp(sn->id_str, name) || !strcmp(sn->name, name)) {
+ *sn_info = *sn;
+ ret = 0;
+ break;
+ }
+ }
+ qemu_free(sn_tab);
+ return ret;
+ }
+
++#ifndef CONFIG_DM
++
+ void do_savevm(const char *name)
+ {
+ BlockDriverState *bs, *bs1;
+ QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1;
+ int must_delete, ret, i;
+ BlockDriverInfo bdi1, *bdi = &bdi1;
+ QEMUFile *f;
+ int saved_vm_running;
+ uint32_t vm_state_size;
+ #ifdef _WIN32
+ struct _timeb tb;
+ #else
+ struct timeval tv;
+ #endif
+
+ bs = get_bs_snapshots();
+ if (!bs) {
+ term_printf("No block device can accept snapshots\n");
+ return;
+ }
+
+ /* ??? Should this occur after vm_stop? */
+ qemu_aio_flush();
+
+ saved_vm_running = vm_running;
+ vm_stop(0);
+
+ must_delete = 0;
+ if (name) {
+ ret = bdrv_snapshot_find(bs, old_sn, name);
+ if (ret >= 0) {
+ must_delete = 1;
+ }
+ }
+ memset(sn, 0, sizeof(*sn));
+ if (must_delete) {
+ pstrcpy(sn->name, sizeof(sn->name), old_sn->name);
+ pstrcpy(sn->id_str, sizeof(sn->id_str), old_sn->id_str);
+ } else {
+ if (name)
+ pstrcpy(sn->name, sizeof(sn->name), name);
+ }
+
+ /* fill auxiliary fields */
+ #ifdef _WIN32
+ _ftime(&tb);
+ sn->date_sec = tb.time;
+ sn->date_nsec = tb.millitm * 1000000;
+ #else
+ gettimeofday(&tv, NULL);
+ sn->date_sec = tv.tv_sec;
+ sn->date_nsec = tv.tv_usec * 1000;
+ #endif
+ sn->vm_clock_nsec = qemu_get_clock(vm_clock);
+
+ if (bdrv_get_info(bs, bdi) < 0 || bdi->vm_state_offset <= 0) {
+ term_printf("Device %s does not support VM state snapshots\n",
+ bdrv_get_device_name(bs));
+ goto the_end;
+ }
+
+ /* save the VM state */
+ f = qemu_fopen_bdrv(bs, bdi->vm_state_offset, 1);
+ if (!f) {
+ term_printf("Could not open VM state file\n");
+ goto the_end;
+ }
+ ret = qemu_savevm_state(f);
+ vm_state_size = qemu_ftell(f);
+ qemu_fclose(f);
+ if (ret < 0) {
+ term_printf("Error %d while writing VM\n", ret);
+ goto the_end;
+ }
+
+ /* create the snapshots */
+
+ for(i = 0; i < nb_drives; i++) {
+ bs1 = drives_table[i].bdrv;
+ if (bdrv_has_snapshot(bs1)) {
+ if (must_delete) {
+ ret = bdrv_snapshot_delete(bs1, old_sn->id_str);
+ if (ret < 0) {
+ term_printf("Error while deleting snapshot on '%s'\n",
+ bdrv_get_device_name(bs1));
+ }
+ }
+ /* Write VM state size only to the image that contains the state */
+ sn->vm_state_size = (bs == bs1 ? vm_state_size : 0);
+ ret = bdrv_snapshot_create(bs1, sn);
+ if (ret < 0) {
+ term_printf("Error while creating snapshot on '%s'\n",
+ bdrv_get_device_name(bs1));
+ }
+ }
+ }
+
+ the_end:
+ if (saved_vm_running)
+ vm_start();
+ }
+
+ void do_loadvm(const char *name)
+ {
+ BlockDriverState *bs, *bs1;
+ BlockDriverInfo bdi1, *bdi = &bdi1;
+ QEMUSnapshotInfo sn;
+ QEMUFile *f;
+ int i, ret;
+ int saved_vm_running;
+
+ bs = get_bs_snapshots();
+ if (!bs) {
+ term_printf("No block device supports snapshots\n");
+ return;
+ }
+
+ /* Flush all IO requests so they don't interfere with the new state. */
+ qemu_aio_flush();
+
+ saved_vm_running = vm_running;
+ vm_stop(0);
+
+ for(i = 0; i <= nb_drives; i++) {
+ bs1 = drives_table[i].bdrv;
+ if (bdrv_has_snapshot(bs1)) {
+ ret = bdrv_snapshot_goto(bs1, name);
+ if (ret < 0) {
+ if (bs != bs1)
+ term_printf("Warning: ");
+ switch(ret) {
+ case -ENOTSUP:
+ term_printf("Snapshots not supported on device '%s'\n",
+ bdrv_get_device_name(bs1));
+ break;
+ case -ENOENT:
+ term_printf("Could not find snapshot '%s' on device '%s'\n",
+ name, bdrv_get_device_name(bs1));
+ break;
+ default:
+ term_printf("Error %d while activating snapshot on '%s'\n",
+ ret, bdrv_get_device_name(bs1));
+ break;
+ }
+ /* fatal on snapshot block device */
+ if (bs == bs1)
+ goto the_end;
+ }
+ }
+ }
+
+ if (bdrv_get_info(bs, bdi) < 0 || bdi->vm_state_offset <= 0) {
+ term_printf("Device %s does not support VM state snapshots\n",
+ bdrv_get_device_name(bs));
+ return;
+ }
+
+ /* Don't even try to load empty VM states */
+ ret = bdrv_snapshot_find(bs, &sn, name);
+ if ((ret >= 0) && (sn.vm_state_size == 0))
+ goto the_end;
+
+ /* restore the VM state */
+ f = qemu_fopen_bdrv(bs, bdi->vm_state_offset, 0);
+ if (!f) {
+ term_printf("Could not open VM state file\n");
+ goto the_end;
+ }
+ ret = qemu_loadvm_state(f);
+ qemu_fclose(f);
+ if (ret < 0) {
+ term_printf("Error %d while loading VM state\n", ret);
+ }
+ the_end:
+ if (saved_vm_running)
+ vm_start();
+ }
+
++#endif /* !CONFIG_DM */
++
+ void do_delvm(const char *name)
+ {
+ BlockDriverState *bs, *bs1;
+ int i, ret;
+
+ bs = get_bs_snapshots();
+ if (!bs) {
+ term_printf("No block device supports snapshots\n");
+ return;
+ }
+
+ for(i = 0; i <= nb_drives; i++) {
+ bs1 = drives_table[i].bdrv;
+ if (bdrv_has_snapshot(bs1)) {
+ ret = bdrv_snapshot_delete(bs1, name);
+ if (ret < 0) {
+ if (ret == -ENOTSUP)
+ term_printf("Snapshots not supported on device '%s'\n",
+ bdrv_get_device_name(bs1));
+ else
+ term_printf("Error %d while deleting snapshot on '%s'\n",
+ ret, bdrv_get_device_name(bs1));
+ }
+ }
+ }
+ }
+
+ void do_info_snapshots(void)
+ {
+ BlockDriverState *bs, *bs1;
+ QEMUSnapshotInfo *sn_tab, *sn;
+ int nb_sns, i;
+ char buf[256];
+
+ bs = get_bs_snapshots();
+ if (!bs) {
+ term_printf("No available block device supports snapshots\n");
+ return;
+ }
+ term_printf("Snapshot devices:");
+ for(i = 0; i <= nb_drives; i++) {
+ bs1 = drives_table[i].bdrv;
+ if (bdrv_has_snapshot(bs1)) {
+ if (bs == bs1)
+ term_printf(" %s", bdrv_get_device_name(bs1));
+ }
+ }
+ term_printf("\n");
+
+ nb_sns = bdrv_snapshot_list(bs, &sn_tab);
+ if (nb_sns < 0) {
+ term_printf("bdrv_snapshot_list: error %d\n", nb_sns);
+ return;
+ }
+ term_printf("Snapshot list (from %s):\n", bdrv_get_device_name(bs));
+ term_printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL));
+ for(i = 0; i < nb_sns; i++) {
+ sn = &sn_tab[i];
+ term_printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn));
+ }
+ qemu_free(sn_tab);
+ }
#include <signal.h>
#endif
-static SDL_Surface *screen;
+#ifdef CONFIG_OPENGL
+#include <SDL_opengl.h>
+#endif
+
- static SDL_Surface *screen;
- static SDL_Surface *shared = NULL;
++static DisplayChangeListener *dcl;
++static SDL_Surface *real_screen;
++static SDL_Surface *guest_screen = NULL;
static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
static int last_vm_running;
static int gui_saved_grab;
static SDL_Cursor *sdl_cursor_normal;
static SDL_Cursor *sdl_cursor_hidden;
static int absolute_enabled = 0;
-static int guest_cursor = 0;
-static int guest_x, guest_y;
-static SDL_Cursor *guest_sprite = 0;
+static int opengl_enabled;
+
- static void sdl_colourdepth(DisplayState *ds, int depth);
-
+#ifdef CONFIG_OPENGL
+static GLint tex_format;
+static GLint tex_type;
+static GLuint texture_ref = 0;
+static GLint gl_format;
++static uint8_t bgr;
+
- static void opengl_setdata(DisplayState *ds, void *pixels)
++static void opengl_setdata(DisplayState *ds)
+{
+ glEnable(GL_TEXTURE_RECTANGLE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glClearColor(0, 0, 0, 0);
+ glDisable(GL_BLEND);
+ glDisable(GL_LIGHTING);
+ glDisable(GL_DEPTH_TEST);
+ glDepthMask(GL_FALSE);
+ glDisable(GL_CULL_FACE);
- glViewport( 0, 0, screen->w, screen->h);
++ glViewport( 0, 0, real_screen->w, real_screen->h);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
- glOrtho(0, screen->w, screen->h, 0, -1,1);
++ glOrtho(0, real_screen->w, real_screen->h, 0, -1,1);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glClear(GL_COLOR_BUFFER_BIT);
- ds->data = pixels;
+
+ if (texture_ref) {
+ glDeleteTextures(1, &texture_ref);
+ texture_ref = 0;
+ }
+
+ glGenTextures(1, &texture_ref);
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_ref);
+ glPixelStorei(GL_UNPACK_LSB_FIRST, 1);
- switch (ds->depth) {
- case 8:
- if (ds->palette == NULL) {
- tex_format = GL_RGB;
- tex_type = GL_UNSIGNED_BYTE_3_3_2;
- } else {
- int i;
- GLushort paletter[256], paletteg[256], paletteb[256];
- for (i = 0; i < 256; i++) {
- uint8_t rgb = ds->palette[i] >> 16;
- paletter[i] = ((rgb & 0xe0) >> 5) * 65535 / 7;
- paletteg[i] = ((rgb & 0x1c) >> 2) * 65535 / 7;
- paletteb[i] = (rgb & 0x3) * 65535 / 3;
- }
- glPixelMapusv(GL_PIXEL_MAP_I_TO_R, 256, paletter);
- glPixelMapusv(GL_PIXEL_MAP_I_TO_G, 256, paletteg);
- glPixelMapusv(GL_PIXEL_MAP_I_TO_B, 256, paletteb);
-
- tex_format = GL_COLOR_INDEX;
- tex_type = GL_UNSIGNED_BYTE;
- }
- break;
++ switch (ds_get_bits_per_pixel(ds)) {
+ case 16:
+ tex_format = GL_RGB;
+ tex_type = GL_UNSIGNED_SHORT_5_6_5;
+ break;
+ case 24:
+ tex_format = GL_BGR;
+ tex_type = GL_UNSIGNED_BYTE;
+ break;
+ case 32:
- if (!ds->bgr) {
++ if (bgr == (ds->surface->pf.rshift < ds->surface->pf.bshift)) {
+ tex_format = GL_BGRA;
+ tex_type = GL_UNSIGNED_BYTE;
+ } else {
+ tex_format = GL_RGBA;
+ tex_type = GL_UNSIGNED_BYTE;
+ }
+ break;
+ }
- glPixelStorei(GL_UNPACK_ROW_LENGTH, (ds->linesize * 8) / ds->depth);
- glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, gl_format, ds->width, ds->height, 0, tex_format, tex_type, pixels);
++ glPixelStorei(GL_UNPACK_ROW_LENGTH, (ds_get_linesize(ds) / ds_get_bytes_per_pixel(ds)));
++ glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, gl_format, ds_get_width(ds), ds_get_height(ds), 0, tex_format, tex_type, ds_get_data(ds));
+ glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_PRIORITY, 1.0);
+ glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
+}
+
+static void opengl_update(DisplayState *ds, int x, int y, int w, int h)
+{
- int bpp = ds->depth / 8;
- GLvoid *pixels = ds->data + y * ds->linesize + x * bpp;
++ int bpp = ds_get_bytes_per_pixel(ds);
++ GLvoid *pixels = ds_get_data(ds) + y * ds_get_linesize(ds) + x * bpp;
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_ref);
- glPixelStorei(GL_UNPACK_ROW_LENGTH, ds->linesize / bpp);
++ glPixelStorei(GL_UNPACK_ROW_LENGTH, ds_get_linesize(ds) / bpp);
+ glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, x, y, w, h, tex_format, tex_type, pixels);
+ glBegin(GL_QUADS);
+ glTexCoord2d(0, 0);
+ glVertex2d(0, 0);
- glTexCoord2d(ds->width, 0);
- glVertex2d(screen->w, 0);
- glTexCoord2d(ds->width, ds->height);
- glVertex2d(screen->w, screen->h);
- glTexCoord2d(0, ds->height);
- glVertex2d(0, screen->h);
++ glTexCoord2d(ds_get_width(ds), 0);
++ glVertex2d(real_screen->w, 0);
++ glTexCoord2d(ds_get_width(ds), ds_get_height(ds));
++ glVertex2d(real_screen->w, real_screen->h);
++ glTexCoord2d(0, ds_get_height(ds));
++ glVertex2d(0, real_screen->h);
+ glEnd();
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
+ SDL_GL_SwapBuffers();
+}
+#endif
static void sdl_update(DisplayState *ds, int x, int y, int w, int h)
{
-- // printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h);
- if (shared) {
- SDL_Rect rec;
- rec.x = x;
- rec.y = y;
- rec.w = w;
- rec.h = h;
- SDL_BlitSurface(shared, &rec, screen, &rec);
- }
- SDL_Flip(screen);
- SDL_UpdateRect(screen, x, y, w, h);
++ SDL_Rect rec;
++ rec.x = x;
++ rec.y = y;
++ rec.w = w;
++ rec.h = h;
++ // printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h);i
++
++ SDL_BlitSurface(guest_screen, &rec, real_screen, &rec);
++ SDL_UpdateRect(real_screen, x, y, w, h);
}
- static void sdl_setdata(DisplayState *ds, void *pixels)
-static void sdl_resize(DisplayState *ds, int w, int h)
++static void sdl_setdata(DisplayState *ds)
+{
- uint32_t rmask, gmask, bmask, amask = 0;
- switch (ds->depth) {
- case 8:
- rmask = 0x000000E0;
- gmask = 0x0000001C;
- bmask = 0x00000003;
- break;
- case 16:
- rmask = 0x0000F800;
- gmask = 0x000007E0;
- bmask = 0x0000001F;
- break;
- case 24:
- rmask = 0x00FF0000;
- gmask = 0x0000FF00;
- bmask = 0x000000FF;
- break;
- case 32:
- rmask = 0x00FF0000;
- gmask = 0x0000FF00;
- bmask = 0x000000FF;
- break;
- default:
- return;
- }
- shared = SDL_CreateRGBSurfaceFrom(pixels, width, height, ds->depth, ds->linesize, rmask , gmask, bmask, amask);
- if (ds->depth == 8 && ds->palette != NULL) {
- SDL_Color palette[256];
- int i;
- for (i = 0; i < 256; i++) {
- uint8_t rgb = ds->palette[i] >> 16;
- palette[i].r = ((rgb & 0xe0) >> 5) * 255 / 7;
- palette[i].g = ((rgb & 0x1c) >> 2) * 255 / 7;
- palette[i].b = (rgb & 0x3) * 255 / 3;
- }
- SDL_SetColors(shared, palette, 0, 256);
- }
- ds->data = pixels;
++ SDL_Rect rec;
++ rec.x = 0;
++ rec.y = 0;
++ rec.w = real_screen->w;
++ rec.h = real_screen->h;
++
++ if (guest_screen != NULL) SDL_FreeSurface(guest_screen);
++
++ guest_screen = SDL_CreateRGBSurfaceFrom(ds_get_data(ds), ds_get_width(ds), ds_get_height(ds),
++ ds_get_bits_per_pixel(ds), ds_get_linesize(ds),
++ ds->surface->pf.rmask, ds->surface->pf.gmask,
++ ds->surface->pf.bmask, ds->surface->pf.amask);
+}
+
- static void sdl_resize_shared(DisplayState *ds, int w, int h, int depth, int linesize, void *pixels)
++static void sdl_resize(DisplayState *ds)
{
int flags;
// printf("resizing to %d %d\n", w, h);
-
- sdl_colourdepth(ds, depth);
-
+#ifdef CONFIG_OPENGL
- if (ds->shared_buf && opengl_enabled)
++ if (opengl_enabled)
+ flags = SDL_OPENGL|SDL_RESIZABLE;
+ else
+#endif
- flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL|SDL_DOUBLEBUF|SDL_HWPALETTE;
++ flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL;
- if (gui_fullscreen) {
- flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL;
+ if (gui_fullscreen)
flags |= SDL_FULLSCREEN;
- flags &= ~SDL_RESIZABLE;
- }
if (gui_noframe)
flags |= SDL_NOFRAME;
-- width = w;
-- height = h;
--
-- again:
-- screen = SDL_SetVideoMode(w, h, 0, flags);
-
-- if (!screen) {
- fprintf(stderr, "Could not open SDL display: %s\n", SDL_GetError());
++ width = ds_get_width(ds);
++ height = ds_get_height(ds);
++ real_screen = SDL_SetVideoMode(width, height, 0, flags);
++ if (!real_screen) {
+ if (opengl_enabled) {
+ /* Fallback to SDL */
+ opengl_enabled = 0;
- ds->dpy_update = sdl_update;
- ds->dpy_setdata = sdl_setdata;
- ds->dpy_resize_shared = sdl_resize_shared;
- sdl_resize_shared(ds, w, h, depth, linesize, pixels);
++ dcl->dpy_update = sdl_update;
++ dcl->dpy_setdata = sdl_setdata;
++ sdl_resize(ds);
+ return;
+ }
+ fprintf(stderr, "Could not open SDL display\n");
exit(1);
}
- if (!screen->pixels && (flags & SDL_HWSURFACE) && (flags & SDL_FULLSCREEN)) {
- flags &= ~SDL_HWSURFACE;
- goto again;
- }
- if (!opengl_enabled) {
- if (!screen->pixels && (flags & SDL_HWSURFACE) && (flags & SDL_FULLSCREEN)) {
- flags &= ~SDL_HWSURFACE;
- goto again;
- }
-
- if (!screen->pixels) {
- fprintf(stderr, "Could not open SDL display: %s\n", SDL_GetError());
- exit(1);
- }
- if (!screen->pixels) {
- fprintf(stderr, "Could not open SDL display\n");
- exit(1);
-- }
- ds->data = screen->pixels;
- ds->linesize = screen->pitch;
- ds->depth = screen->format->BitsPerPixel;
- /* SDL BitsPerPixel never indicates any values other than
- multiples of 8, so we need to check for strange depths. */
- if (ds->depth == 16) {
- uint32_t mask;
--
- ds->width = w;
- ds->height = h;
- if (!ds->shared_buf) {
- ds->depth = screen->format->BitsPerPixel;
- if (screen->format->Bshift > screen->format->Rshift) {
- ds->bgr = 1;
- } else {
- ds->bgr = 0;
- }
- shared = NULL;
- ds->data = screen->pixels;
- ds->linesize = screen->pitch;
- } else {
- ds->linesize = linesize;
- mask = screen->format->Rmask;
- mask |= screen->format->Gmask;
- mask |= screen->format->Bmask;
- if ((mask & 0x8000) == 0)
- ds->depth = 15;
- }
- if (ds->depth == 32 && screen->format->Rshift == 0) {
- ds->bgr = 1;
+#ifdef CONFIG_OPENGL
- switch(screen->format->BitsPerPixel) {
++ if (real_screen->format->Bshift > real_screen->format->Rshift) {
++ bgr = 1;
+ } else {
- ds->bgr = 0;
++ bgr = 0;
+ }
- ds->width = w;
- ds->height = h;
++ switch(real_screen->format->BitsPerPixel) {
+ case 8:
+ gl_format = GL_RGB;
+ break;
+ case 16:
+ gl_format = GL_RGB;
+ break;
+ case 24:
+ gl_format = GL_RGB;
+ break;
+ case 32:
- if (!screen->format->Rshift)
++ if (!real_screen->format->Rshift)
+ gl_format = GL_BGRA;
+ else
+ gl_format = GL_RGBA;
+ break;
- };
++ };
+#endif
- }
- if (ds->shared_buf) ds->dpy_setdata(ds, pixels);
- }
+
- static void sdl_resize(DisplayState *ds, int w, int h)
- {
- sdl_resize_shared(ds, w, h, 0, w * (ds->depth / 8), NULL);
- }
-
- static void sdl_colourdepth(DisplayState *ds, int depth)
- {
- if (!depth || !ds->depth) {
- ds->shared_buf = 0;
- ds->dpy_update = sdl_update;
- return;
- }
- ds->shared_buf = 1;
- ds->depth = depth;
- #ifdef CONFIG_OPENGL
- if (opengl_enabled) {
- ds->dpy_update = opengl_update;
- }
- #endif
++ dcl->dpy_setdata(ds);
}
/* generic keyboard conversion */
absolute_enabled = 1;
}
- dx = x * 0x7FFF / (width - 1);
- dy = y * 0x7FFF / (height - 1);
+ SDL_GetMouseState(&dx, &dy);
- dx = dx * 0x7FFF / (screen->w - 1);
- dy = dy * 0x7FFF / (screen->h - 1);
++ dx = dx * 0x7FFF / (real_screen->w - 1);
++ dy = dy * 0x7FFF / (real_screen->h - 1);
} else if (absolute_enabled) {
sdl_show_cursor();
absolute_enabled = 0;
static void toggle_full_screen(DisplayState *ds)
{
gui_fullscreen = !gui_fullscreen;
- sdl_resize_shared(ds, ds->width, ds->height, ds->depth, ds->linesize, ds->data);
- sdl_resize(ds, screen->w, screen->h);
++ sdl_resize(ds);
if (gui_fullscreen) {
gui_saved_grab = gui_grab;
sdl_grab_start();
while (SDL_PollEvent(ev)) {
switch (ev->type) {
case SDL_VIDEOEXPOSE:
- ds->dpy_update(ds, 0, 0, ds->width, ds->height);
- sdl_update(ds, 0, 0, screen->w, screen->h);
++ dcl->dpy_update(ds, 0, 0, ds_get_width(ds), ds_get_height(ds));
break;
case SDL_KEYDOWN:
case SDL_KEYUP:
!ev->active.gain && !gui_fullscreen_initial_grab) {
sdl_grab_end();
}
- if (ev->active.state & SDL_APPACTIVE) {
- if (ev->active.gain) {
- /* Back to default interval */
- ds->gui_timer_interval = 0;
- ds->idle = 0;
- } else {
- /* Sleeping interval */
- ds->gui_timer_interval = 500;
- ds->idle = 1;
- }
+ if (ev->active.state & SDL_APPACTIVE) {
+ if (ev->active.gain) {
+ /* Back to default interval */
- ds->gui_timer_interval = 0;
- ds->idle = 0;
++ dcl->gui_timer_interval = 0;
++ dcl->idle = 0;
+ } else {
+ /* Sleeping interval */
- ds->gui_timer_interval = 500;
- ds->idle = 1;
++ dcl->gui_timer_interval = 500;
++ dcl->idle = 1;
+ }
+ }
+ break;
+#ifdef CONFIG_OPENGL
+ case SDL_VIDEORESIZE:
+ {
- if (ds->shared_buf && opengl_enabled) {
++ if (opengl_enabled) {
+ SDL_ResizeEvent *rev = &ev->resize;
- screen = SDL_SetVideoMode(rev->w, rev->h, 0, SDL_OPENGL|SDL_RESIZABLE);
- opengl_setdata(ds, ds->data);
- opengl_update(ds, 0, 0, ds->width, ds->height);
++ real_screen = SDL_SetVideoMode(rev->w, rev->h, 0, SDL_OPENGL|SDL_RESIZABLE);
++ opengl_setdata(ds);
++ opengl_update(ds, 0, 0, ds_get_width(ds), ds_get_height(ds));
}
break;
+ }
+#endif
default:
break;
}
fprintf(stderr, "Could not initialize SDL - exiting\n");
exit(1);
}
+#ifndef _WIN32
+ /* NOTE: we still want Ctrl-C to work, so we undo the SDL redirections */
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+#endif
-- ds->dpy_update = sdl_update;
-- ds->dpy_resize = sdl_resize;
- ds->dpy_resize_shared = sdl_resize_shared;
-- ds->dpy_refresh = sdl_refresh;
- ds->dpy_setdata = sdl_setdata;
- ds->dpy_fill = sdl_fill;
- ds->mouse_set = sdl_mouse_warp;
- ds->cursor_define = sdl_mouse_define;
++ dcl = qemu_mallocz(sizeof(DisplayChangeListener));
++ if (!dcl)
++ exit(1);
++ dcl->dpy_update = sdl_update;
++ dcl->dpy_resize = sdl_resize;
++ dcl->dpy_refresh = sdl_refresh;
++ dcl->dpy_setdata = sdl_setdata;
+#ifdef CONFIG_OPENGL
- if (opengl_enabled)
- ds->dpy_setdata = opengl_setdata;
++ if (opengl_enabled) {
++ dcl->dpy_update = opengl_update;
++ dcl->dpy_setdata = opengl_setdata;
++ }
+#endif
++ register_displaychangelistener(ds, dcl);
-- sdl_resize(ds, 640, 400);
sdl_update_caption();
SDL_EnableKeyRepeat(250, 50);
gui_grab = 0;
#endif
typedef enum {
- IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD
+ IF_BLKTAP,
+ IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO
} BlockInterfaceType;
typedef struct DriveInfo {
#include "gdbstub.h"
#include "qemu-timer.h"
#include "qemu-char.h"
+ #include "cache-utils.h"
#include "block.h"
#include "audio/audio.h"
+#include "qemu-xen.h"
#include "migration.h"
+ #include "kvm.h"
+ #include "balloon.h"
#include <unistd.h>
+#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>
to store the VM snapshots */
DriveInfo drives_table[MAX_DRIVES+1];
int nb_drives;
+/* point to the block driver where the snapshots are managed */
+static BlockDriverState *bs_snapshots;
+ static int vga_ram_size;
enum vga_retrace_method vga_retrace_method = VGA_RETRACE_DUMB;
- static DisplayState display_state;
+ DisplayState display_state;
int nographic;
static int curses;
const char* keyboard_layout = NULL;
int graphic_depth = 15;
#endif
static int full_screen = 0;
+ #ifdef CONFIG_SDL
static int no_frame = 0;
+ #endif
int no_quit = 0;
+#ifdef CONFIG_OPENGL
+int opengl_enabled = 1;
+#else
+int opengl_enabled = 0;
+#endif
+static const char *direct_pci;
+static int nb_pci_emulation = 0;
+static char pci_emulation_config_text[MAX_PCI_EMULATION][256];
+PCI_EMULATION_INFO *PciEmulationInfoHead = NULL;
CharDriverState *serial_hds[MAX_SERIAL_PORTS];
CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
+ CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES];
#ifdef TARGET_I386
int win2k_install_hack = 0;
+ int rtc_td_hack = 0;
#endif
int usb_enabled = 0;
- static VLANState *first_vlan;
int smp_cpus = 1;
const char *vnc_display;
+int vncunused;
int acpi_enabled = 1;
+ int no_hpet = 0;
int fd_bootchk = 1;
int no_reboot = 0;
int no_shutdown = 0;
uint8_t qemu_uuid[16];
- #define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR)
-
+#include "xen-vl-extra.c"
+
- typedef struct IOHandlerRecord {
- int fd;
- IOCanRWHandler *fd_read_poll;
- IOHandler *fd_read;
- IOHandler *fd_write;
- int deleted;
- void *opaque;
- /* temporary data */
- struct pollfd *ufd;
- struct IOHandlerRecord *next;
- } IOHandlerRecord;
-
- static IOHandlerRecord *first_io_handler;
-
/***********************************************************/
/* x86 ISA bus support */
return 0;
}
- #ifndef CONFIG_DM
++#ifndef CONFIG_DM /* ends after end of win32_rearm_timer */
#ifdef _WIN32
void CALLBACK host_alarm_handler(UINT uTimerID, UINT uMsg,
DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
}
#endif /* _WIN32 */
+#endif /* !CONFIG_DM */
- static void init_timer_alarm(void)
+ static int init_timer_alarm(void)
{
struct qemu_alarm_timer *t = NULL;
int i, err = -1;
" hostname 'host' to DHCP clients\n"
#endif
#ifdef _WIN32
- "-net tap[,vlan=n],ifname=name\n"
+ "-net tap[,vlan=n][,name=str],ifname=name\n"
" connect the host TAP network interface to VLAN 'n'\n"
#else
- "-net tap[,vlan=n][,fd=h][,ifname=name][,script=file][,downscript=dfile][,scriptarg=extraargument]\n"
- "-net tap[,vlan=n][,name=str][,fd=h][,ifname=name][,script=file][,downscript=dfile]\n"
++ "-net tap[,vlan=n][,name=str][,fd=h][,ifname=name][,script=file][,downscript=dfile][,scriptarg=extraargument]\n"
" connect the host TAP network interface to VLAN 'n' and use the\n"
" network scripts 'file' (default=%s)\n"
" and 'dfile' (default=%s);\n"
" use '[down]script=no' to disable script execution;\n"
+ " use 'scriptarg=...' to pass an additional (nonempty) argument;\n"
" use 'fd=h' to connect to an already opened TAP interface\n"
#endif
- "-net socket[,vlan=n][,fd=h][,listen=[host]:port][,connect=host:port]\n"
+ "-net socket[,vlan=n][,name=str][,fd=h][,listen=[host]:port][,connect=host:port]\n"
" connect the vlan 'n' to another VLAN using a socket connection\n"
- "-net socket[,vlan=n][,fd=h][,mcast=maddr:port]\n"
+ "-net socket[,vlan=n][,name=str][,fd=h][,mcast=maddr:port]\n"
" connect the vlan 'n' to multicast maddr and port\n"
#ifdef CONFIG_VDE
- "-net vde[,vlan=n][,sock=socketpath][,port=n][,group=groupname][,mode=octalmode]\n"
+ "-net vde[,vlan=n][,name=str][,sock=socketpath][,port=n][,group=groupname][,mode=octalmode]\n"
" connect the vlan 'n' to port 'n' of a vde switch running\n"
" on host and listening for incoming connections on 'socketpath'.\n"
" Use group 'groupname' and mode 'octalmode' to change default\n"
"-kernel-kqemu enable KQEMU full virtualization (default is user mode only)\n"
"-no-kqemu disable KQEMU kernel module usage\n"
#endif
+ #ifdef CONFIG_KVM
+ "-enable-kvm enable KVM full virtualization support\n"
+ #endif
#ifdef TARGET_I386
+ "-std-vga simulate a standard VGA card with VESA Bochs Extensions\n"
+ " (default is CL-GD5446 PCI VGA)\n"
+ "-videoram set amount of memory available to virtual video adapter\n"
"-no-acpi disable ACPI\n"
+ "-no-hpet disable HPET\n"
#endif
#ifdef CONFIG_CURSES
"-curses use a curses/ncurses interface instead of SDL\n"
QEMU_OPTION_localtime,
QEMU_OPTION_g,
QEMU_OPTION_vga,
+ QEMU_OPTION_std_vga,
+ QEMU_OPTION_videoram,
QEMU_OPTION_echr,
QEMU_OPTION_monitor,
+ QEMU_OPTION_domainname,
QEMU_OPTION_serial,
+ QEMU_OPTION_virtiocon,
QEMU_OPTION_parallel,
QEMU_OPTION_loadvm,
QEMU_OPTION_full_screen,
QEMU_OPTION_usbdevice,
QEMU_OPTION_smp,
QEMU_OPTION_vnc,
+ QEMU_OPTION_vncunused,
QEMU_OPTION_no_acpi,
+ QEMU_OPTION_no_hpet,
QEMU_OPTION_curses,
QEMU_OPTION_no_reboot,
QEMU_OPTION_no_shutdown,
{ "vga", HAS_ARG, QEMU_OPTION_vga },
{ "echr", HAS_ARG, QEMU_OPTION_echr },
{ "monitor", HAS_ARG, QEMU_OPTION_monitor },
+ { "domain-name", 1, QEMU_OPTION_domainname },
{ "serial", HAS_ARG, QEMU_OPTION_serial },
+ { "virtioconsole", HAS_ARG, QEMU_OPTION_virtiocon },
{ "parallel", HAS_ARG, QEMU_OPTION_parallel },
{ "loadvm", HAS_ARG, QEMU_OPTION_loadvm },
{ "full-screen", 0, QEMU_OPTION_full_screen },
{ "alt-grab", 0, QEMU_OPTION_alt_grab },
{ "no-quit", 0, QEMU_OPTION_no_quit },
#endif
+#ifdef CONFIG_OPENGL
+ { "disable-opengl", 0, QEMU_OPTION_disable_opengl },
+#endif
+ { "vcpus", 1, QEMU_OPTION_vcpus },
+ { "acpi", 0, QEMU_OPTION_acpi }, /* deprecated, for xend compatibility */
+ { "direct_pci", HAS_ARG, QEMU_OPTION_direct_pci },
+ { "pciemulation", HAS_ARG, QEMU_OPTION_pci_emulation },
{ "pidfile", HAS_ARG, QEMU_OPTION_pidfile },
{ "win2k-hack", 0, QEMU_OPTION_win2k_hack },
+ { "rtc-td-hack", 0, QEMU_OPTION_rtc_td_hack },
{ "usbdevice", HAS_ARG, QEMU_OPTION_usbdevice },
{ "smp", HAS_ARG, QEMU_OPTION_smp },
{ "vnc", HAS_ARG, QEMU_OPTION_vnc },
const char *usb_devices[MAX_USB_CMDLINE];
int usb_devices_index;
int fds[2];
+#ifndef CONFIG_STUBDOM
+ struct rlimit rl;
+#endif
int tb_size;
const char *pid_file = NULL;
- VLANState *vlan;
int autostart;
const char *incoming = NULL;
+ logfile = stderr; /* initial value */
+
+#if !defined(__sun__) && !defined(CONFIG_STUBDOM)
+ /* Maximise rlimits. Needed where default constraints are tight (*BSD). */
+ if (getrlimit(RLIMIT_STACK, &rl) != 0) {
+ perror("getrlimit(RLIMIT_STACK)");
+ exit(1);
+ }
+ rl.rlim_cur = rl.rlim_max;
+ if (setrlimit(RLIMIT_STACK, &rl) != 0)
+ perror("setrlimit(RLIMIT_STACK)");
+ if (getrlimit(RLIMIT_DATA, &rl) != 0) {
+ perror("getrlimit(RLIMIT_DATA)");
+ exit(1);
+ }
+ rl.rlim_cur = rl.rlim_max;
+ if (setrlimit(RLIMIT_DATA, &rl) != 0)
+ perror("setrlimit(RLIMIT_DATA)");
+ rl.rlim_cur = RLIM_INFINITY;
+ rl.rlim_max = RLIM_INFINITY;
+ if (setrlimit(RLIMIT_RSS, &rl) != 0)
+ perror("setrlimit(RLIMIT_RSS)");
+ rl.rlim_cur = RLIM_INFINITY;
+ rl.rlim_max = RLIM_INFINITY;
+ if (setrlimit(RLIMIT_MEMLOCK, &rl) != 0)
+ perror("setrlimit(RLIMIT_MEMLOCK)");
+ #endif
+
+ qemu_cache_utils_init(envp);
+
LIST_INIT (&vm_change_state_head);
#ifndef _WIN32
{
machine = first_machine;
cpu_model = NULL;
initrd_filename = NULL;
-- ram_size = 0;
- vga_ram_size = VGA_RAM_SIZE;
++ ram_size = VGA_RAM_SIZE;
#ifdef CONFIG_GDBSTUB
use_gdbstub = 0;
gdbstub_port = DEFAULT_GDBSTUB_PORT;
case QEMU_OPTION_semihosting:
semihosting_enabled = 1;
break;
+ case QEMU_OPTION_domainname: /* depricated, use -name instead */
case QEMU_OPTION_name:
qemu_name = optarg;
+ snprintf(domain_name, sizeof(domain_name),
+ "Xen-%s", optarg);
break;
- #ifdef TARGET_SPARC
+ #if defined(TARGET_SPARC) || defined(TARGET_PPC)
case QEMU_OPTION_prom_env:
if (nb_prom_envs >= MAX_PROM_ENVS) {
fprintf(stderr, "Too many prom variables\n");
if (net_client_parse(net_clients[i]) < 0)
exit(1);
}
- for(vlan = first_vlan; vlan != NULL; vlan = vlan->next) {
- if (vlan->nb_guest_devs == 0 && vlan->nb_host_devs == 0)
- continue;
- if (vlan->nb_guest_devs == 0)
- fprintf(stderr, "Warning: vlan %d with no nics\n", vlan->id);
- if (vlan->nb_host_devs == 0)
- fprintf(stderr,
- "Warning: vlan %d is not connected to host network\n",
- vlan->id);
- }
+ net_client_check();
-#ifdef TARGET_I386
+#if defined(TARGET_I386) && !defined(CONFIG_DM)
/* XXX: this should be moved in the PC machine instantiation code */
if (net_boot != 0) {
int netroms = 0;
exit(1);
}
}
+
+#endif
+
+#if defined (__ia64__)
+ if (ram_size > VGA_IO_START)
+ ram_size += VGA_IO_SIZE; /* skip VGA I/O hole */
+ if (ram_size > MMIO_START)
+ ram_size += 1 * MEM_G; /* skip 3G-4G MMIO, LEGACY_IO_SPACE etc. */
#endif
+ /* init the bluetooth world */
+ for (i = 0; i < nb_bt_opts; i++)
+ if (bt_parse(bt_opts[i]))
+ exit(1);
+
/* init the memory */
- phys_ram_size = machine->ram_require & ~RAMSIZE_FIXED;
+
+ /* If we're on cirrus, set vga_ram_size to 4M whatever the videoram option might have set it to */
+ if ( cirrus_vga_enabled && vga_ram_size != 4 * 1024 * 1024 )
+ {
+ fprintf(stderr,"-videoram option does not work with cirrus vga device model. Videoram set to 4M.\n");
+ vga_ram_size = 4 * 1024 * 1024;
+ }
+
+ phys_ram_size = (machine->ram_require + vga_ram_size) & ~RAMSIZE_FIXED;
if (machine->ram_require & RAMSIZE_FIXED) {
if (ram_size > 0) {
}
}
+ for (i = 0; i < nb_pci_emulation; i++) {
+ if(pci_emulation_add(pci_emulation_config_text[i]) < 0) {
+ fprintf(stderr, "Warning: could not add PCI device %s\n",
+ pci_emulation_config_text[i]);
+ }
+ }
+
+ if (strlen(direct_pci_str) > 0)
+ direct_pci = direct_pci_str;
+
+ for(i = 0; i < MAX_VIRTIO_CONSOLES; i++) {
+ const char *devname = virtio_consoles[i];
+ if (devname && strcmp(devname, "none")) {
+ char label[32];
+ snprintf(label, sizeof(label), "virtcon%d", i);
+ virtcon_hds[i] = qemu_chr_open(label, devname);
+ if (!virtcon_hds[i]) {
+ fprintf(stderr, "qemu: could not open virtio console '%s'\n",
+ devname);
+ exit(1);
+ }
+ if (strstart(devname, "vc", 0))
+ qemu_chr_printf(virtcon_hds[i], "virtio console%d\r\n", i);
+ }
+ }
+
+ if (kvm_enabled()) {
+ int ret;
+
+ ret = kvm_init(smp_cpus);
+ if (ret < 0) {
+ fprintf(stderr, "failed to initialize KVM\n");
+ exit(1);
+ }
+ }
+
machine->init(ram_size, vga_ram_size, boot_devices, ds,
- kernel_filename, kernel_cmdline, initrd_filename, cpu_model);
+ kernel_filename, kernel_cmdline, initrd_filename, cpu_model,
+ direct_pci);
+ /* Set KVM's vcpu state to qemu's initial CPUState. */
+ if (kvm_enabled()) {
+ int ret;
+
+ ret = kvm_sync_vcpus();
+ if (ret < 0) {
+ fprintf(stderr, "failed to initialize vcpus\n");
+ exit(1);
+ }
+ }
+
/* init USB devices */
if (usb_enabled) {
for(i = 0; i < usb_devices_index; i++) {
#define VNC_DEBUG(fmt, ...) do { } while (0)
#endif
++#define count_bits(c, v) { \
++ for (c = 0; v; v >>= 1) \
++ { \
++ c += v & 1; \
++ } \
++}
typedef struct Buffer
{
int lsock;
int csock;
DisplayState *ds;
- int need_update;
-- int width;
-- int height;
- uint32_t dirty_row[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS];
- char *old_data;
- int depth; /* internal VNC frame buffer byte per pixel */
+ uint64_t *dirty_row; /* screen regions which are possibly dirty */
+ int dirty_pixel_shift;
+ uint64_t *update_row; /* outstanding updates */
+ int has_update; /* there's outstanding updates in the
+ * visible area */
+
+ int update_requested; /* the client requested an update */
+
+ uint8_t *old_data;
- int depth; /* internal VNC frame buffer byte per pixel */
int has_resize;
int has_hextile;
int has_pointer_type_change;
/* current output mode information */
VncWritePixels *write_pixels;
VncSendHextileTile *send_hextile_tile;
-- int pix_bpp, pix_big_endian;
- int red_shift, red_max, red_shift1, red_max1;
- int green_shift, green_max, green_shift1, green_max1;
- int blue_shift, blue_max, blue_shift1, blue_max1;
- int client_red_shift, client_red_max, server_red_shift, server_red_max;
- int client_green_shift, client_green_max, server_green_shift, server_green_max;
- int client_blue_shift, client_blue_max, server_blue_shift, server_blue_max;
-
- CaptureVoiceOut *audio_cap;
- struct audsettings as;
++ DisplaySurface clientds, serverds;
VncReadEvent *read_handler;
size_t read_handler_expect;
};
static VncState *vnc_state; /* needed for info vnc */
++static DisplayChangeListener *dcl;
+
+#define DIRTY_PIXEL_BITS 64
+#define X2DP_DOWN(vs, x) ((x) >> (vs)->dirty_pixel_shift)
+#define X2DP_UP(vs, x) \
+ (((x) + (1ULL << (vs)->dirty_pixel_shift) - 1) >> (vs)->dirty_pixel_shift)
+#define DP2X(vs, x) ((x) << (vs)->dirty_pixel_shift)
void do_info_vnc(void)
{
static void vnc_write_u16(VncState *vs, uint16_t value);
static void vnc_write_u8(VncState *vs, uint8_t value);
static void vnc_flush(VncState *vs);
+static void _vnc_update_client(void *opaque);
static void vnc_update_client(void *opaque);
static void vnc_client_read(void *opaque);
-
-static void vnc_colordepth(DisplayState *ds, int depth);
-
+static void framebuffer_set_updated(VncState *vs, int x, int y, int w, int h);
+static void pixel_format_message (VncState *vs);
+static void enqueue_framebuffer_update(VncState *vs, int x, int y, int w, int h, int32_t encoding);
+static void dequeue_framebuffer_update(VncState *vs);
+static int is_empty_queue(VncState *vs);
+static void free_queue(VncState *vs);
- static void vnc_colourdepth(DisplayState *ds, int depth);
++static void vnc_colordepth(DisplayState *ds);
+
+#if 0
static inline void vnc_set_bit(uint32_t *d, int k)
{
d[k >> 5] |= 1 << (k & 0x1f);
}
return 0;
}
+#endif
-static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h)
+static void set_bits_in_row(VncState *vs, uint64_t *row,
+ int x, int y, int w, int h)
{
- VncState *vs = ds->opaque;
- int i;
+ int x1, x2;
+ uint64_t mask;
- h += y;
+ if (w == 0)
+ return;
- /* round x down to ensure the loop only spans one 16-pixel block per,
- iteration. otherwise, if (x % 16) != 0, the last iteration may span
- two 16-pixel blocks but we only mark the first as dirty
- */
- w += (x % 16);
- x -= (x % 16);
+ x1 = X2DP_DOWN(vs, x);
+ x2 = X2DP_UP(vs, x + w);
- x = MIN(x, vs->width);
- y = MIN(y, vs->height);
- w = MIN(x + w, vs->width) - x;
- h = MIN(h, vs->height);
+ if (X2DP_UP(vs, w) != DIRTY_PIXEL_BITS)
+ mask = ((1ULL << (x2 - x1)) - 1) << x1;
+ else
+ mask = ~(0ULL);
- if (h > vs->ds->height)
- h = vs->ds->height;
+ h += y;
++ if (h > ds_get_height(vs->ds))
++ h = ds_get_height(vs->ds);
for (; y < h; y++)
- for (i = 0; i < w; i += 16)
- vnc_set_bit(vs->dirty_row[y], (x + i) / 16);
+ row[y] |= mask;
+}
+
+static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h)
+{
+ VncState *vs = ds->opaque;
+
- x = MIN(x, vs->width);
- y = MIN(y, vs->height);
- w = MIN(w, vs->width - x);
- h = MIN(h, vs->height - y);
++ x = MIN(x, vs->serverds.width);
++ y = MIN(y, vs->serverds.height);
++ w = MIN(w, vs->serverds.width - x);
++ h = MIN(h, vs->serverds.height - y);
+
+ set_bits_in_row(vs, vs->dirty_row, x, y, w, h);
}
static void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
vnc_write_s32(vs, encoding);
}
- static void vnc_dpy_resize_shared(DisplayState *ds, int w, int h, int depth, int linesize, void *pixels)
-static void vnc_dpy_resize(DisplayState *ds, int w, int h)
++static void vnc_dpy_resize(DisplayState *ds)
{
- static int allocated;
int size_changed;
VncState *vs = ds->opaque;
+ int o;
- vnc_colourdepth(ds, depth);
- if (!ds->shared_buf) {
- ds->linesize = w * vs->depth;
- if (allocated)
- ds->data = qemu_realloc(ds->data, h * ds->linesize);
- else
- ds->data = malloc(h * ds->linesize);
- allocated = 1;
- } else {
- ds->linesize = linesize;
- if (allocated) {
- free(ds->data);
- allocated = 0;
- }
- }
- vs->old_data = qemu_realloc(vs->old_data, h * ds->linesize);
- vs->dirty_row = qemu_realloc(vs->dirty_row, h * sizeof(vs->dirty_row[0]));
- vs->update_row = qemu_realloc(vs->update_row, h * sizeof(vs->dirty_row[0]));
- ds->data = qemu_realloc(ds->data, w * h * vs->depth);
- vs->old_data = qemu_realloc(vs->old_data, w * h * vs->depth);
++ vs->old_data = qemu_realloc(vs->old_data, ds_get_height(ds) * ds_get_linesize(ds));
++ vs->dirty_row = qemu_realloc(vs->dirty_row, ds_get_height(ds) * sizeof(vs->dirty_row[0]));
++ vs->update_row = qemu_realloc(vs->update_row, ds_get_height(ds) * sizeof(vs->dirty_row[0]));
- if (ds->data == NULL || vs->old_data == NULL ||
- vs->dirty_row == NULL || vs->update_row == NULL) {
- if (ds->data == NULL || vs->old_data == NULL) {
++ if (vs->old_data == NULL || vs->dirty_row == NULL || vs->update_row == NULL) {
fprintf(stderr, "vnc: memory allocation failed\n");
exit(1);
}
-- if (ds->depth != vs->depth * 8) {
-- ds->depth = vs->depth * 8;
++ if (ds_get_bytes_per_pixel(ds) != vs->serverds.pf.bytes_per_pixel)
console_color_init(ds);
-- }
-- size_changed = ds->width != w || ds->height != h;
-- ds->width = w;
-- ds->height = h;
- ds->linesize = w * vs->depth;
- if (size_changed) {
- vs->width = ds->width;
- vs->height = ds->height;
- if (vs->csock != -1 && vs->has_resize) {
- vnc_write_u8(vs, 0); /* msg id */
- vnc_write_u8(vs, 0);
- vnc_write_u16(vs, 1); /* number of rects */
- vnc_framebuffer_update(vs, 0, 0, ds->width, ds->height, -223);
- vnc_flush(vs);
++ vnc_colordepth(ds);
++ size_changed = ds_get_width(ds) != vs->serverds.width ||
++ ds_get_height(ds) != vs->serverds.height;
++ vs->serverds = *(ds->surface);
+ if (vs->csock != -1 && vs->has_resize && size_changed) {
- vs->width = ds->width;
- vs->height = ds->height;
+ if (vs->update_requested) {
+ vnc_write_u8(vs, 0); /* msg id */
+ vnc_write_u8(vs, 0);
+ vnc_write_u16(vs, 1); /* number of rects */
- vnc_framebuffer_update(vs, 0, 0, ds->width, ds->height, -223);
++ vnc_framebuffer_update(vs, 0, 0, ds_get_width(ds), ds_get_height(ds), -223);
+ vnc_flush(vs);
+ vs->update_requested--;
+ } else {
- enqueue_framebuffer_update(vs, 0, 0, ds->width, ds->height, -223);
++ enqueue_framebuffer_update(vs, 0, 0, ds_get_width(ds), ds_get_height(ds), -223);
}
}
-
- memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row));
- memset(vs->old_data, 42, ds_get_linesize(vs->ds) * ds_get_height(vs->ds));
+ vs->dirty_pixel_shift = 0;
- for (o = DIRTY_PIXEL_BITS; o < ds->width; o *= 2)
++ for (o = DIRTY_PIXEL_BITS; o < ds_get_width(ds); o *= 2)
+ vs->dirty_pixel_shift++;
- framebuffer_set_updated(vs, 0, 0, ds->width, ds->height);
- if (ds->shared_buf) ds->data = pixels;
- }
-
- static void vnc_dpy_resize(DisplayState *ds, int w, int h)
- {
- vnc_dpy_resize_shared(ds, w, h, 0, w * (ds->depth / 8), NULL);
++ framebuffer_set_updated(vs, 0, 0, ds_get_width(ds), ds_get_height(ds));
}
/* fastest code */
{
uint8_t r, g, b;
- r = ((v >> vs->red_shift1) & vs->red_max1) * (vs->red_max + 1) / (vs->red_max1 + 1);
- g = ((v >> vs->green_shift1) & vs->green_max1) * (vs->green_max + 1) / (vs->green_max1 + 1);
- b = ((v >> vs->blue_shift1) & vs->blue_max1) * (vs->blue_max + 1) / (vs->blue_max1 + 1);
- r = ((v >> vs->server_red_shift) & vs->server_red_max) * (vs->client_red_max + 1) /
- (vs->server_red_max + 1);
- g = ((v >> vs->server_green_shift) & vs->server_green_max) * (vs->client_green_max + 1) /
- (vs->server_green_max + 1);
- b = ((v >> vs->server_blue_shift) & vs->server_blue_max) * (vs->client_blue_max + 1) /
- (vs->server_blue_max + 1);
- v = (r << vs->client_red_shift) |
- (g << vs->client_green_shift) |
- (b << vs->client_blue_shift);
-- switch(vs->pix_bpp) {
++ r = ((((v & vs->serverds.pf.rmask) >> vs->serverds.pf.rshift) << vs->clientds.pf.rbits) >>
++ vs->serverds.pf.rbits);
++ g = ((((v & vs->serverds.pf.gmask) >> vs->serverds.pf.gshift) << vs->clientds.pf.gbits) >>
++ vs->serverds.pf.gbits);
++ b = ((((v & vs->serverds.pf.bmask) >> vs->serverds.pf.bshift) << vs->clientds.pf.bbits) >>
++ vs->serverds.pf.bbits);
++ v = (r << vs->clientds.pf.rshift) |
++ (g << vs->clientds.pf.gshift) |
++ (b << vs->clientds.pf.bshift);
++ switch(vs->clientds.pf.bytes_per_pixel) {
case 1:
- buf[0] = (r << vs->red_shift) | (g << vs->green_shift) | (b << vs->blue_shift);
- buf[0] = v;
++ buf[0] = v;
break;
case 2:
- {
- uint16_t *p = (uint16_t *) buf;
- *p = (r << vs->red_shift) | (g << vs->green_shift) | (b << vs->blue_shift);
-- if (vs->pix_big_endian) {
- *p = htons(*p);
++ if (vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) {
+ buf[0] = v >> 8;
+ buf[1] = v;
+ } else {
+ buf[1] = v >> 8;
+ buf[0] = v;
}
- }
break;
default:
case 4:
- {
- uint32_t *p = (uint32_t *) buf;
- *p = (r << vs->red_shift) | (g << vs->green_shift) | (b << vs->blue_shift);
-- if (vs->pix_big_endian) {
- *p = htonl(*p);
++ if (vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) {
+ buf[0] = v >> 24;
+ buf[1] = v >> 16;
+ buf[2] = v >> 8;
+ buf[3] = v;
+ } else {
+ buf[3] = v >> 24;
+ buf[2] = v >> 16;
+ buf[1] = v >> 8;
+ buf[0] = v;
}
break;
}
{
uint8_t buf[4];
-- if (vs->depth == 4) {
++ if (vs->serverds.pf.bytes_per_pixel == 4) {
uint32_t *pixels = pixels1;
int n, i;
n = size >> 2;
for(i = 0; i < n; i++) {
vnc_convert_pixel(vs, buf, pixels[i]);
-- vnc_write(vs, buf, vs->pix_bpp);
++ vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel);
}
-- } else if (vs->depth == 2) {
++ } else if (vs->serverds.pf.bytes_per_pixel == 2) {
uint16_t *pixels = pixels1;
int n, i;
n = size >> 1;
for(i = 0; i < n; i++) {
vnc_convert_pixel(vs, buf, pixels[i]);
-- vnc_write(vs, buf, vs->pix_bpp);
++ vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel);
}
-- } else if (vs->depth == 1) {
++ } else if (vs->serverds.pf.bytes_per_pixel == 1) {
uint8_t *pixels = pixels1;
int n, i;
n = size;
for(i = 0; i < n; i++) {
vnc_convert_pixel(vs, buf, pixels[i]);
-- vnc_write(vs, buf, vs->pix_bpp);
++ vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel);
}
} else {
fprintf(stderr, "vnc_write_pixels_generic: VncState color depth not supported\n");
vnc_framebuffer_update(vs, x, y, w, h, 0);
- row = vs->ds->data + y * vs->ds->linesize + x * vs->depth;
- row = ds_get_data(vs->ds) + y * ds_get_linesize(vs->ds) + x * vs->depth;
++ row = ds_get_data(vs->ds) + y * ds_get_linesize(vs->ds) + x * ds_get_bytes_per_pixel(vs->ds);
for (i = 0; i < h; i++) {
-- vs->write_pixels(vs, row, w * vs->depth);
- row += vs->ds->linesize;
++ vs->write_pixels(vs, row, w * ds_get_bytes_per_pixel(vs->ds));
+ row += ds_get_linesize(vs->ds);
}
}
vnc_framebuffer_update(vs, x, y, w, h, 5);
- last_fg = (void *) malloc(vs->depth);
- last_bg = (void *) malloc(vs->depth);
- last_fg = (uint8_t *) malloc(vs->depth);
- last_bg = (uint8_t *) malloc(vs->depth);
++ last_fg = (void *) malloc(vs->serverds.pf.bytes_per_pixel);
++ last_bg = (void *) malloc(vs->serverds.pf.bytes_per_pixel);
has_fg = has_bg = 0;
for (j = y; j < (y + h); j += 16) {
for (i = x; i < (x + w); i += 16) {
static void vnc_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
{
-- int src, dst;
-- uint8_t *src_row;
-- uint8_t *dst_row;
- uint8_t *old_row;
- char *old_row;
-- int y = 0;
- int pitch = ds->linesize;
- int pitch = ds_get_linesize(ds);
VncState *vs = ds->opaque;
+ int updating_client = 1;
- if (ds->shared_buf) {
- framebuffer_set_updated(vs, dst_x, dst_y, w, h);
- return;
- vnc_update_client(vs);
-
- if (dst_y > src_y) {
- y = h - 1;
- pitch = -pitch;
-- }
-
- src = (ds_get_linesize(ds) * (src_y + y) + vs->depth * src_x);
- dst = (ds_get_linesize(ds) * (dst_y + y) + vs->depth * dst_x);
--
- src_row = ds_get_data(ds) + src;
- dst_row = ds_get_data(ds) + dst;
- old_row = vs->old_data + dst;
+ if (!vs->update_requested ||
+ src_x < vs->visible_x || src_y < vs->visible_y ||
+ dst_x < vs->visible_x || dst_y < vs->visible_y ||
+ (src_x + w) > (vs->visible_x + vs->visible_w) ||
+ (src_y + h) > (vs->visible_y + vs->visible_h) ||
+ (dst_x + w) > (vs->visible_x + vs->visible_w) ||
+ (dst_y + h) > (vs->visible_y + vs->visible_h))
+ updating_client = 0;
- for (y = 0; y < h; y++) {
- memmove(old_row, src_row, w * vs->depth);
- memmove(dst_row, src_row, w * vs->depth);
- src_row += pitch;
- dst_row += pitch;
- old_row += pitch;
- }
+ if (updating_client)
- _vnc_update_client(vs);
-
- if (dst_y > src_y) {
- y = h - 1;
- pitch = -pitch;
- }
-
- src = (ds->linesize * (src_y + y) + vs->depth * src_x);
- dst = (ds->linesize * (dst_y + y) + vs->depth * dst_x);
-
- src_row = ds->data + src;
- dst_row = ds->data + dst;
- old_row = vs->old_data + dst;
-
- for (y = 0; y < h; y++) {
- memmove(old_row, src_row, w * vs->depth);
- memmove(dst_row, src_row, w * vs->depth);
- src_row += pitch;
- dst_row += pitch;
- old_row += pitch;
- }
++ _vnc_update_client(vs);
- vnc_write_u8(vs, 0); /* msg id */
- vnc_write_u8(vs, 0);
- vnc_write_u16(vs, 1); /* number of rects */
- vnc_framebuffer_update(vs, dst_x, dst_y, w, h, 1);
- vnc_write_u16(vs, src_x);
- vnc_write_u16(vs, src_y);
- vnc_flush(vs);
+ if (updating_client && vs->csock != -1 && !vs->has_update) {
+ vnc_write_u8(vs, 0); /* msg id */
+ vnc_write_u8(vs, 0);
+ vnc_write_u16(vs, 1); /* number of rects */
+ vnc_framebuffer_update(vs, dst_x, dst_y, w, h, 1);
+ vnc_write_u16(vs, src_x);
+ vnc_write_u16(vs, src_y);
+ vnc_flush(vs);
+ vs->update_requested--;
+ } else
+ framebuffer_set_updated(vs, dst_x, dst_y, w, h);
}
-static int find_dirty_height(VncState *vs, int y, int last_x, int x)
+static int find_update_height(VncState *vs, int y, int maxy, int last_x, int x)
{
int h;
return h;
}
-static void vnc_update_client(void *opaque)
+static void _vnc_update_client(void *opaque)
{
VncState *vs = opaque;
- int tile_bytes = vs->depth * DP2X(vs, 1);
+ int64_t now;
+ int y;
+ uint8_t *row;
+ uint8_t *old_row;
+ uint64_t width_mask;
+ int n_rectangles;
+ int saved_offset;
+ int maxx, maxy;
++ int tile_bytes = vs->serverds.pf.bytes_per_pixel * DP2X(vs, 1);
+
+ if (!vs->update_requested || vs->csock == -1)
+ return;
+ while (!is_empty_queue(vs) && vs->update_requested) {
+ int enc = vs->upqueue.queue_end->enc;
+ dequeue_framebuffer_update(vs);
+ switch (enc) {
+ case 0x574D5669:
+ pixel_format_message(vs);
+ break;
+ default:
+ break;
+ }
+ vs->update_requested--;
+ }
+ if (!vs->update_requested) return;
- if (vs->need_update && vs->csock != -1) {
- int y;
- uint8_t *row;
- char *old_row;
- uint32_t width_mask[VNC_DIRTY_WORDS];
- int n_rectangles;
- int saved_offset;
- int has_dirty = 0;
-
- vga_hw_update();
-
- vnc_set_bits(width_mask, (vs->width / 16), VNC_DIRTY_WORDS);
-
- /* Walk through the dirty map and eliminate tiles that
- really aren't dirty */
- row = ds_get_data(vs->ds);
- old_row = vs->old_data;
-
- for (y = 0; y < vs->height; y++) {
- if (vnc_and_bits(vs->dirty_row[y], width_mask, VNC_DIRTY_WORDS)) {
- int x;
- uint8_t *ptr;
- char *old_ptr;
-
- ptr = row;
- old_ptr = (char*)old_row;
-
- for (x = 0; x < ds_get_width(vs->ds); x += 16) {
- if (memcmp(old_ptr, ptr, 16 * vs->depth) == 0) {
- vnc_clear_bit(vs->dirty_row[y], (x / 16));
- } else {
- has_dirty = 1;
- memcpy(old_ptr, ptr, 16 * vs->depth);
- }
+ now = qemu_get_clock(rt_clock);
- if (vs->width != DP2X(vs, DIRTY_PIXEL_BITS))
- width_mask = (1ULL << X2DP_UP(vs, vs->ds->width)) - 1;
- ptr += 16 * vs->depth;
- old_ptr += 16 * vs->depth;
- }
- }
++ if (vs->serverds.width != DP2X(vs, DIRTY_PIXEL_BITS))
++ width_mask = (1ULL << X2DP_UP(vs, ds_get_width(vs->ds))) - 1;
+ else
+ width_mask = ~(0ULL);
- row += ds_get_linesize(vs->ds);
- old_row += ds_get_linesize(vs->ds);
- }
+ /* Walk through the dirty map and eliminate tiles that really
+ aren't dirty */
- row = vs->ds->data;
++ row = ds_get_data(vs->ds);
+ old_row = vs->old_data;
- for (y = 0; y < vs->ds->height; y++) {
- if (!has_dirty && !vs->audio_cap) {
- qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL);
- return;
- }
++ for (y = 0; y < ds_get_height(vs->ds); y++) {
+ if (vs->dirty_row[y] & width_mask) {
+ int x;
+ uint8_t *ptr, *old_ptr;
- /* Count rectangles */
- n_rectangles = 0;
- vnc_write_u8(vs, 0); /* msg id */
- vnc_write_u8(vs, 0);
- saved_offset = vs->output.offset;
- vnc_write_u16(vs, 0);
+ ptr = row;
+ old_ptr = old_row;
- for (x = 0; x < X2DP_UP(vs, vs->ds->width); x++) {
- for (y = 0; y < vs->height; y++) {
- int x;
- int last_x = -1;
- for (x = 0; x < vs->width / 16; x++) {
- if (vnc_get_bit(vs->dirty_row[y], x)) {
- if (last_x == -1) {
- last_x = x;
++ for (x = 0; x < X2DP_UP(vs, ds_get_width(vs->ds)); x++) {
+ if (vs->dirty_row[y] & (1ULL << x)) {
+ if (memcmp(old_ptr, ptr, tile_bytes)) {
+ vs->has_update = 1;
+ vs->update_row[y] |= (1ULL << x);
+ memcpy(old_ptr, ptr, tile_bytes);
}
- vnc_clear_bit(vs->dirty_row[y], x);
- } else {
- if (last_x != -1) {
- int h = find_dirty_height(vs, y, last_x, x);
- send_framebuffer_update(vs, last_x * 16, y, (x - last_x) * 16, h);
+ vs->dirty_row[y] &= ~(1ULL << x);
+ }
+
+ ptr += tile_bytes;
+ old_ptr += tile_bytes;
+ }
+ }
+
- row += vs->ds->linesize;
- old_row += vs->ds->linesize;
++ row += ds_get_linesize(vs->ds);
++ old_row += ds_get_linesize(vs->ds);
+ }
+
- if (!vs->has_update || vs->visible_y >= vs->ds->height ||
- vs->visible_x >= vs->ds->width)
++ if (!vs->has_update || vs->visible_y >= ds_get_height(vs->ds) ||
++ vs->visible_x >= ds_get_width(vs->ds))
+ goto backoff;
+
+ /* Count rectangles */
+ n_rectangles = 0;
+ vnc_write_u8(vs, 0); /* msg id */
+ vnc_write_u8(vs, 0);
+ saved_offset = vs->output.offset;
+ vnc_write_u16(vs, 0);
+
+ maxy = vs->visible_y + vs->visible_h;
- if (maxy > vs->ds->height)
- maxy = vs->ds->height;
++ if (maxy > ds_get_height(vs->ds))
++ maxy = ds_get_height(vs->ds);
+ maxx = vs->visible_x + vs->visible_w;
- if (maxx > vs->ds->width)
- maxx = vs->ds->width;
++ if (maxx > ds_get_width(vs->ds))
++ maxx = ds_get_width(vs->ds);
+
+ for (y = vs->visible_y; y < maxy; y++) {
+ int x;
+ int last_x = -1;
+ for (x = X2DP_DOWN(vs, vs->visible_x);
+ x < X2DP_UP(vs, maxx); x++) {
+ if (vs->update_row[y] & (1ULL << x)) {
+ if (last_x == -1)
+ last_x = x;
+ vs->update_row[y] &= ~(1ULL << x);
+ } else {
+ if (last_x != -1) {
+ int h = find_update_height(vs, y, maxy, last_x, x);
+ if (h != 0) {
+ send_framebuffer_update(vs, DP2X(vs, last_x), y,
+ DP2X(vs, (x - last_x)), h);
n_rectangles++;
}
- last_x = -1;
}
+ last_x = -1;
}
- if (last_x != -1) {
- int h = find_dirty_height(vs, y, last_x, x);
- send_framebuffer_update(vs, last_x * 16, y, (x - last_x) * 16, h);
+ }
+ if (last_x != -1) {
+ int h = find_update_height(vs, y, maxy, last_x, x);
+ if (h != 0) {
+ send_framebuffer_update(vs, DP2X(vs, last_x), y,
+ DP2X(vs, (x - last_x)), h);
n_rectangles++;
}
}
- vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
- vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
- vnc_flush(vs);
-
}
+ vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
+ vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
- if (vs->csock != -1) {
- qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL);
+ if (n_rectangles == 0) {
+ vs->output.offset = saved_offset - 2;
+ goto backoff;
+ } else
+ vs->update_requested--;
+
+ vs->has_update = 0;
+ vnc_flush(vs);
+ vs->last_update_time = now;
- vs->ds->idle = 0;
++ dcl->idle = 0;
+
+ vs->timer_interval /= 2;
+ if (vs->timer_interval < VNC_REFRESH_INTERVAL_BASE)
+ vs->timer_interval = VNC_REFRESH_INTERVAL_BASE;
+
+ return;
+
+ backoff:
+ /* No update -> back off a bit */
+ vs->timer_interval += VNC_REFRESH_INTERVAL_INC;
+ if (vs->timer_interval > VNC_REFRESH_INTERVAL_MAX) {
+ vs->timer_interval = VNC_REFRESH_INTERVAL_MAX;
+ if (now - vs->last_update_time >= VNC_MAX_UPDATE_INTERVAL) {
+ if (!vs->update_requested) {
- vs->ds->idle = 1;
++ dcl->idle = 1;
+ } else {
+ /* Send a null update. If the client is no longer
+ interested (e.g. minimised) it'll ignore this, and we
+ can stop scanning the buffer until it sends another
+ update request. */
+ /* It turns out that there's a bug in realvncviewer 4.1.2
+ which means that if you send a proper null update (with
+ no update rectangles), it gets a bit out of sync and
+ never sends any further requests, regardless of whether
+ it needs one or not. Fix this by sending a single 1x1
+ update rectangle instead. */
+ vnc_write_u8(vs, 0);
+ vnc_write_u8(vs, 0);
+ vnc_write_u16(vs, 1);
+ send_framebuffer_update(vs, 0, 0, 1, 1);
+ vnc_flush(vs);
+ vs->last_update_time = now;
+ vs->update_requested--;
+ return;
+ }
+ }
}
+ qemu_mod_timer(vs->timer, now + vs->timer_interval);
+ return;
+}
+
+static void vnc_update_client(void *opaque)
+{
+ VncState *vs = opaque;
+
+ vga_hw_update();
+ _vnc_update_client(vs);
+}
+static void vnc_timer_init(VncState *vs)
+{
+ if (vs->timer == NULL) {
+ vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs);
+ vs->timer_interval = VNC_REFRESH_INTERVAL_BASE;
+ }
}
static int vnc_listen_poll(void *opaque)
qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);
closesocket(vs->csock);
vs->csock = -1;
-- vs->ds->idle = 1;
++ dcl->idle = 1;
buffer_reset(&vs->input);
buffer_reset(&vs->output);
- vs->need_update = 0;
+ free_queue(vs);
+ vs->update_requested = 0;
#ifdef CONFIG_VNC_TLS
if (vs->tls_session) {
gnutls_deinit(vs->tls_session);
static void check_pointer_type_change(VncState *vs, int absolute)
{
if (vs->has_pointer_type_change && vs->absolute != absolute) {
- vnc_write_u8(vs, 0);
- vnc_write_u8(vs, 0);
- vnc_write_u16(vs, 1);
- vnc_framebuffer_update(vs, absolute, 0,
+ if (vs->update_requested) {
+ vnc_write_u8(vs, 0);
+ vnc_write_u8(vs, 0);
+ vnc_write_u16(vs, 1);
+ vnc_framebuffer_update(vs, absolute, 0,
- vs->ds->width, vs->ds->height, -257);
+ ds_get_width(vs->ds), ds_get_height(vs->ds), -257);
- vnc_flush(vs);
+ vnc_flush(vs);
+ vs->update_requested--;
+ } else {
+ enqueue_framebuffer_update(vs, absolute, 0,
- vs->ds->width, vs->ds->height, -257);
++ ds_get_width(vs->ds), ds_get_height(vs->ds), -257);
+ }
}
vs->absolute = absolute;
}
dz = 1;
if (vs->absolute) {
- kbd_mouse_event(x * 0x7FFF / (vs->ds->width - 1),
- y * 0x7FFF / (vs->ds->height - 1),
- kbd_mouse_event(x * 0x7FFF / (ds_get_width(vs->ds) - 1),
- y * 0x7FFF / (ds_get_height(vs->ds) - 1),
++ kbd_mouse_event(x * 0x7FFF / (ds_get_width(vs->ds) - 1),
++ y * 0x7FFF / (ds_get_height(vs->ds) - 1),
dz, buttons);
} else if (vs->has_pointer_type_change) {
x -= 0x7FFF;
vs->has_pointer_type_change = 0;
vs->has_WMVi = 0;
vs->absolute = -1;
-- vs->ds->dpy_copy = NULL;
++ dcl->dpy_copy = NULL;
for (i = n_encodings - 1; i >= 0; i--) {
switch (encodings[i]) {
vs->has_hextile = 0;
break;
case 1: /* CopyRect */
-- vs->ds->dpy_copy = vnc_copy;
++ dcl->dpy_copy = vnc_copy;
break;
case 5: /* Hextile */
vs->has_hextile = 1;
check_pointer_type_change(vs, kbd_mouse_is_absolute());
}
++static void set_pixel_conversion(VncState *vs)
++{
++ if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) ==
++ (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG) &&
++ !memcmp(&(vs->clientds.pf), &(vs->ds->surface->pf), sizeof(PixelFormat))) {
++ vs->write_pixels = vnc_write_pixels_copy;
++ switch (vs->ds->surface->pf.bits_per_pixel) {
++ case 8:
++ vs->send_hextile_tile = send_hextile_tile_8;
++ break;
++ case 16:
++ vs->send_hextile_tile = send_hextile_tile_16;
++ break;
++ case 32:
++ vs->send_hextile_tile = send_hextile_tile_32;
++ break;
++ }
++ } else {
++ vs->write_pixels = vnc_write_pixels_generic;
++ switch (vs->ds->surface->pf.bits_per_pixel) {
++ case 8:
++ vs->send_hextile_tile = send_hextile_tile_generic_8;
++ break;
++ case 16:
++ vs->send_hextile_tile = send_hextile_tile_generic_16;
++ break;
++ case 32:
++ vs->send_hextile_tile = send_hextile_tile_generic_32;
++ break;
++ }
++ }
++}
++
static void set_pixel_format(VncState *vs,
int bits_per_pixel, int depth,
int big_endian_flag, int true_color_flag,
int red_max, int green_max, int blue_max,
int red_shift, int green_shift, int blue_shift)
{
-- int host_big_endian_flag;
--
--#ifdef WORDS_BIGENDIAN
-- host_big_endian_flag = 1;
--#else
-- host_big_endian_flag = 0;
--#endif
if (!true_color_flag) {
-- fail:
vnc_client_error(vs);
return;
}
-- if (bits_per_pixel == 32 &&
- bits_per_pixel == vs->depth * 8 &&
- bits_per_pixel == vs->depth * 8 &&
-- host_big_endian_flag == big_endian_flag &&
-- red_max == 0xff && green_max == 0xff && blue_max == 0xff &&
-- red_shift == 16 && green_shift == 8 && blue_shift == 0) {
- vs->depth = 4;
-- vs->write_pixels = vnc_write_pixels_copy;
-- vs->send_hextile_tile = send_hextile_tile_32;
- } else
- } else
-- if (bits_per_pixel == 16 &&
-- bits_per_pixel == vs->depth * 8 &&
-- host_big_endian_flag == big_endian_flag &&
-- red_max == 31 && green_max == 63 && blue_max == 31 &&
-- red_shift == 11 && green_shift == 5 && blue_shift == 0) {
- vs->depth = 2;
-- vs->write_pixels = vnc_write_pixels_copy;
-- vs->send_hextile_tile = send_hextile_tile_16;
- } else
- if (bits_per_pixel == 8 &&
- } else
- if (bits_per_pixel == 8 &&
-- bits_per_pixel == vs->depth * 8 &&
-- red_max == 7 && green_max == 7 && blue_max == 3 &&
-- red_shift == 5 && green_shift == 2 && blue_shift == 0) {
-- vs->depth = 1;
-- vs->write_pixels = vnc_write_pixels_copy;
-- vs->send_hextile_tile = send_hextile_tile_8;
- } else
- } else
-- {
-- /* generic and slower case */
-- if (bits_per_pixel != 8 &&
-- bits_per_pixel != 16 &&
-- bits_per_pixel != 32)
-- goto fail;
-- if (vs->depth == 4) {
-- vs->send_hextile_tile = send_hextile_tile_generic_32;
-- } else if (vs->depth == 2) {
- vs->send_hextile_tile = send_hextile_tile_generic_16;
- vs->send_hextile_tile = send_hextile_tile_generic_16;
-- } else {
-- vs->send_hextile_tile = send_hextile_tile_generic_8;
-- }
-
-
-- vs->pix_big_endian = big_endian_flag;
-- vs->write_pixels = vnc_write_pixels_generic;
-- }
-
- vs->red_shift = red_shift;
- vs->red_max = red_max;
- vs->green_shift = green_shift;
- vs->green_max = green_max;
- vs->blue_shift = blue_shift;
- vs->blue_max = blue_max;
- vs->pix_bpp = bits_per_pixel / 8;
+
- vs->client_red_shift = red_shift;
- vs->client_red_max = red_max;
- vs->client_green_shift = green_shift;
- vs->client_green_max = green_max;
- vs->client_blue_shift = blue_shift;
- vs->client_blue_max = blue_max;
- vs->pix_bpp = bits_per_pixel / 8;
++ vs->clientds = vs->serverds;
++ vs->clientds.pf.rmax = red_max;
++ count_bits(vs->clientds.pf.rbits, red_max);
++ vs->clientds.pf.rshift = red_shift;
++ vs->clientds.pf.rmask = red_max << red_shift;
++ vs->clientds.pf.gmax = green_max;
++ count_bits(vs->clientds.pf.gbits, green_max);
++ vs->clientds.pf.gshift = green_shift;
++ vs->clientds.pf.gmask = green_max << green_shift;
++ vs->clientds.pf.bmax = blue_max;
++ count_bits(vs->clientds.pf.bbits, blue_max);
++ vs->clientds.pf.bshift = blue_shift;
++ vs->clientds.pf.bmask = blue_max << blue_shift;
++ vs->clientds.pf.bits_per_pixel = bits_per_pixel;
++ vs->clientds.pf.bytes_per_pixel = bits_per_pixel / 8;
++ vs->clientds.pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel;
++ vs->clientds.flags = big_endian_flag ? QEMU_BIG_ENDIAN_FLAG : 0x00;
++
++ set_pixel_conversion(vs);
+
+ vga_hw_invalidate();
+ vga_hw_update();
}
static void pixel_format_message (VncState *vs) {
char pad[3] = { 0, 0, 0 };
-- vnc_write_u8(vs, vs->depth * 8); /* bits-per-pixel */
-- if (vs->depth == 4) vnc_write_u8(vs, 24); /* depth */
-- else vnc_write_u8(vs, vs->depth * 8); /* depth */
++ vnc_write_u8(vs, vs->ds->surface->pf.bits_per_pixel); /* bits-per-pixel */
++ vnc_write_u8(vs, vs->ds->surface->pf.depth); /* depth */
#ifdef WORDS_BIGENDIAN
vnc_write_u8(vs, 1); /* big-endian-flag */
vnc_write_u8(vs, 0); /* big-endian-flag */
#endif
vnc_write_u8(vs, 1); /* true-color-flag */
-- if (vs->depth == 4) {
-- vnc_write_u16(vs, 0xFF); /* red-max */
-- vnc_write_u16(vs, 0xFF); /* green-max */
-- vnc_write_u16(vs, 0xFF); /* blue-max */
-- vnc_write_u8(vs, 16); /* red-shift */
-- vnc_write_u8(vs, 8); /* green-shift */
-- vnc_write_u8(vs, 0); /* blue-shift */
++ vnc_write_u16(vs, vs->ds->surface->pf.rmax); /* red-max */
++ vnc_write_u16(vs, vs->ds->surface->pf.gmax); /* green-max */
++ vnc_write_u16(vs, vs->ds->surface->pf.bmax); /* blue-max */
++ vnc_write_u8(vs, vs->ds->surface->pf.rshift); /* red-shift */
++ vnc_write_u8(vs, vs->ds->surface->pf.gshift); /* green-shift */
++ vnc_write_u8(vs, vs->ds->surface->pf.bshift); /* blue-shift */
++ if (vs->ds->surface->pf.bits_per_pixel == 32)
vs->send_hextile_tile = send_hextile_tile_32;
-- } else if (vs->depth == 2) {
-- vnc_write_u16(vs, 31); /* red-max */
-- vnc_write_u16(vs, 63); /* green-max */
-- vnc_write_u16(vs, 31); /* blue-max */
-- vnc_write_u8(vs, 11); /* red-shift */
-- vnc_write_u8(vs, 5); /* green-shift */
-- vnc_write_u8(vs, 0); /* blue-shift */
++ else if (vs->ds->surface->pf.bits_per_pixel == 16)
vs->send_hextile_tile = send_hextile_tile_16;
-- } else if (vs->depth == 1) {
-- /* XXX: change QEMU pixel 8 bit pixel format to match the VNC one ? */
-- vnc_write_u16(vs, 7); /* red-max */
-- vnc_write_u16(vs, 7); /* green-max */
-- vnc_write_u16(vs, 3); /* blue-max */
-- vnc_write_u8(vs, 5); /* red-shift */
-- vnc_write_u8(vs, 2); /* green-shift */
-- vnc_write_u8(vs, 0); /* blue-shift */
++ else if (vs->ds->surface->pf.bits_per_pixel == 8)
vs->send_hextile_tile = send_hextile_tile_8;
-- }
- vs->red_max = vs->red_max1;
- vs->green_max = vs->green_max1;
- vs->blue_max = vs->blue_max1;
- vs->red_shift = vs->red_shift1;
- vs->green_shift = vs->green_shift1;
- vs->blue_shift = vs->blue_shift1;
- vs->client_red_max = vs->server_red_max;
- vs->client_green_max = vs->server_green_max;
- vs->client_blue_max = vs->server_blue_max;
- vs->client_red_shift = vs->server_red_shift;
- vs->client_green_shift = vs->server_green_shift;
- vs->client_blue_shift = vs->server_blue_shift;
-- vs->pix_bpp = vs->depth * 8;
++ vs->clientds = *(vs->ds->surface);
++ vs->clientds.flags &= ~QEMU_ALLOCATED_FLAG;
vs->write_pixels = vnc_write_pixels_copy;
-
+
vnc_write(vs, pad, 3); /* padding */
}
- static void vnc_dpy_setdata(DisplayState *ds, void *pixels)
-static void vnc_colordepth(DisplayState *ds, int depth)
++static void vnc_dpy_setdata(DisplayState *ds)
{
- ds->data = pixels;
- int host_big_endian_flag;
- struct VncState *vs = ds->opaque;
-
- switch (depth) {
- case 24:
- if (ds->depth == 32) return;
- depth = 32;
- break;
- case 15:
- case 8:
- case 0:
- return;
- default:
- break;
- }
++ /* We don't have to do anything */
+}
- static void vnc_colourdepth(DisplayState *ds, int depth)
-#ifdef WORDS_BIGENDIAN
- host_big_endian_flag = 1;
-#else
- host_big_endian_flag = 0;
-#endif
-
- switch (depth) {
- case 8:
- vs->depth = depth / 8;
- vs->server_red_max = 7;
- vs->server_green_max = 7;
- vs->server_blue_max = 3;
- vs->server_red_shift = 5;
- vs->server_green_shift = 2;
- vs->server_blue_shift = 0;
- break;
- case 16:
- vs->depth = depth / 8;
- vs->server_red_max = 31;
- vs->server_green_max = 63;
- vs->server_blue_max = 31;
- vs->server_red_shift = 11;
- vs->server_green_shift = 5;
- vs->server_blue_shift = 0;
- break;
- case 32:
- vs->depth = 4;
- vs->server_red_max = 255;
- vs->server_green_max = 255;
- vs->server_blue_max = 255;
- vs->server_red_shift = 16;
- vs->server_green_shift = 8;
- vs->server_blue_shift = 0;
- break;
- default:
- return;
- }
++static void vnc_colordepth(DisplayState *ds)
+{
- int host_big_endian_flag;
+ struct VncState *vs = ds->opaque;
- switch (depth) {
- case 24:
- ds->shared_buf = 0;
- if (ds->depth == 32) return;
- depth = 32;
- break;
- case 8:
- case 0:
- ds->shared_buf = 0;
- return;
- default:
- ds->shared_buf = 1;
- break;
- }
-
- #ifdef WORDS_BIGENDIAN
- host_big_endian_flag = 1;
- #else
- host_big_endian_flag = 0;
- #endif
-
- switch (depth) {
- case 8:
- vs->depth = depth / 8;
- vs->red_max1 = 7;
- vs->green_max1 = 7;
- vs->blue_max1 = 3;
- vs->red_shift1 = 5;
- vs->green_shift1 = 2;
- vs->blue_shift1 = 0;
- break;
- case 16:
- vs->depth = depth / 8;
- vs->red_max1 = 31;
- vs->green_max1 = 63;
- vs->blue_max1 = 31;
- vs->red_shift1 = 11;
- vs->green_shift1 = 5;
- vs->blue_shift1 = 0;
- break;
- case 32:
- vs->depth = 4;
- vs->red_max1 = 255;
- vs->green_max1 = 255;
- vs->blue_max1 = 255;
- vs->red_shift1 = 16;
- vs->green_shift1 = 8;
- vs->blue_shift1 = 0;
- break;
- default:
- return;
- }
- if (vs->csock != -1 && vs->has_WMVi) {
+ if (vs->switchbpp) {
+ vnc_client_error(vs);
+ } else if (vs->csock != -1 && vs->has_WMVi) {
/* Sending a WMVi message to notify the client*/
- vnc_write_u8(vs, 0); /* msg id */
- vnc_write_u8(vs, 0);
- vnc_write_u16(vs, 1); /* number of rects */
- vnc_framebuffer_update(vs, 0, 0, ds->width, ds->height, 0x574D5669);
- pixel_format_message(vs);
- vnc_flush(vs);
- } else {
- if (vs->pix_bpp == 4 && vs->depth == 4 &&
- host_big_endian_flag == vs->pix_big_endian &&
- vs->client_red_max == 0xff && vs->client_green_max == 0xff && vs->client_blue_max == 0xff &&
- vs->client_red_shift == 16 && vs->client_green_shift == 8 && vs->client_blue_shift == 0) {
- vs->write_pixels = vnc_write_pixels_copy;
- vs->send_hextile_tile = send_hextile_tile_32;
- } else if (vs->pix_bpp == 2 && vs->depth == 2 &&
- host_big_endian_flag == vs->pix_big_endian &&
- vs->client_red_max == 31 && vs->client_green_max == 63 && vs->client_blue_max == 31 &&
- vs->client_red_shift == 11 && vs->client_green_shift == 5 && vs->client_blue_shift == 0) {
- vs->write_pixels = vnc_write_pixels_copy;
- vs->send_hextile_tile = send_hextile_tile_16;
- } else if (vs->pix_bpp == 1 && vs->depth == 1 &&
- host_big_endian_flag == vs->pix_big_endian &&
- vs->client_red_max == 7 && vs->client_green_max == 7 && vs->client_blue_max == 3 &&
- vs->client_red_shift == 5 && vs->client_green_shift == 2 && vs->client_blue_shift == 0) {
- vs->write_pixels = vnc_write_pixels_copy;
- vs->send_hextile_tile = send_hextile_tile_8;
+ if (vs->update_requested) {
+ vnc_write_u8(vs, 0); /* msg id */
+ vnc_write_u8(vs, 0);
+ vnc_write_u16(vs, 1); /* number of rects */
- vnc_framebuffer_update(vs, 0, 0, ds->width, ds->height, 0x574D5669);
++ vnc_framebuffer_update(vs, 0, 0, ds_get_width(ds), ds_get_height(ds), 0x574D5669);
+ pixel_format_message(vs);
+ vnc_flush(vs);
+ vs->update_requested--;
} else {
- enqueue_framebuffer_update(vs, 0, 0, ds->width, ds->height, 0x574D5669);
- if (vs->depth == 4) {
- vs->send_hextile_tile = send_hextile_tile_generic_32;
- } else if (vs->depth == 2) {
- vs->send_hextile_tile = send_hextile_tile_generic_16;
- } else {
- vs->send_hextile_tile = send_hextile_tile_generic_8;
- }
- vs->write_pixels = vnc_write_pixels_generic;
++ enqueue_framebuffer_update(vs, 0, 0, ds_get_width(ds), ds_get_height(ds), 0x574D5669);
}
- if (vs->pix_bpp == 4 && vs->depth == 4 &&
- host_big_endian_flag == vs->pix_big_endian &&
- vs->red_max == 0xff && vs->green_max == 0xff && vs->blue_max == 0xff &&
- vs->red_shift == 16 && vs->green_shift == 8 && vs->blue_shift == 0) {
- vs->write_pixels = vnc_write_pixels_copy;
- vs->send_hextile_tile = send_hextile_tile_32;
- } else if (vs->pix_bpp == 2 && vs->depth == 2 &&
- host_big_endian_flag == vs->pix_big_endian &&
- vs->red_max == 31 && vs->green_max == 63 && vs->blue_max == 31 &&
- vs->red_shift == 11 && vs->green_shift == 5 && vs->blue_shift == 0) {
- vs->write_pixels = vnc_write_pixels_copy;
- vs->send_hextile_tile = send_hextile_tile_16;
- } else if (vs->pix_bpp == 1 && vs->depth == 1 &&
- host_big_endian_flag == vs->pix_big_endian &&
- vs->red_max == 7 && vs->green_max == 7 && vs->blue_max == 3 &&
- vs->red_shift == 5 && vs->green_shift == 2 && vs->blue_shift == 0) {
- vs->write_pixels = vnc_write_pixels_copy;
- vs->send_hextile_tile = send_hextile_tile_8;
- } else {
- if (vs->depth == 4) {
- vs->send_hextile_tile = send_hextile_tile_generic_32;
- } else if (vs->depth == 2) {
- vs->send_hextile_tile = send_hextile_tile_generic_16;
- } else {
- vs->send_hextile_tile = send_hextile_tile_generic_8;
- }
- vs->write_pixels = vnc_write_pixels_generic;
- }
+ } else {
++ set_pixel_conversion(vs);
}
}
static int protocol_client_init(VncState *vs, uint8_t *data, size_t len)
{
- char buf[1024];
- int size;
+ size_t l;
+
+ vga_hw_update();
- vs->width = vs->ds->width;
- vs->height = vs->ds->height;
- vnc_write_u16(vs, vs->ds->width);
- vnc_write_u16(vs, vs->ds->height);
- vs->width = ds_get_width(vs->ds);
- vs->height = ds_get_height(vs->ds);
+ vnc_write_u16(vs, ds_get_width(vs->ds));
+ vnc_write_u16(vs, ds_get_height(vs->ds));
pixel_format_message(vs);
vs->csock = accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
if (vs->csock != -1) {
- vnc_connect(vs);
+ VNC_DEBUG("New client on socket %d\n", vs->csock);
- vs->ds->idle = 0;
++ dcl->idle = 0;
+ socket_set_nonblock(vs->csock);
+ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, opaque);
+ vnc_write(vs, "RFB 003.008\n", 12);
+ vnc_flush(vs);
+ vnc_read_when(vs, protocol_version, 12);
- framebuffer_set_updated(vs, 0, 0, vs->ds->width, vs->ds->height);
++ framebuffer_set_updated(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds));
+ vs->has_resize = 0;
+ vs->has_hextile = 0;
+ vs->update_requested = 0;
- vs->ds->dpy_copy = NULL;
++ dcl->dpy_copy = NULL;
+ vnc_timer_init(vs);
}
}
VncState *vs;
vs = qemu_mallocz(sizeof(VncState));
-- if (!vs)
++ dcl = qemu_mallocz(sizeof(DisplayChangeListener));
++ if (!vs || !dcl)
exit(1);
ds->opaque = vs;
-- ds->idle = 1;
++ dcl->idle = 1;
vnc_state = vs;
vs->display = NULL;
vs->password = NULL;
vs->ds = ds;
- if (keyboard_layout)
- vs->kbd_layout = init_keyboard_layout(keyboard_layout);
- else
- vs->kbd_layout = init_keyboard_layout("en-us");
+ if (!keyboard_layout)
+ keyboard_layout = "en-us";
+ vs->kbd_layout = init_keyboard_layout(keyboard_layout);
if (!vs->kbd_layout)
exit(1);
+ vs->modifiers_state[0x45] = 1; /* NumLock on - on boot */
- vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs);
-
-- vs->ds->data = NULL;
-- vs->ds->dpy_update = vnc_dpy_update;
-- vs->ds->dpy_resize = vnc_dpy_resize;
- vs->ds->dpy_setdata = vnc_dpy_setdata;
- vs->ds->dpy_resize_shared = vnc_dpy_resize_shared;
-- vs->ds->dpy_refresh = NULL;
--
- vs->ds->width = 640;
- vs->ds->height = 400;
- vs->ds->linesize = 640 * 4;
- vnc_dpy_resize_shared(ds, ds->width, ds->height, 24, ds->linesize, NULL);
- vnc_colordepth(vs->ds, 32);
- vnc_dpy_resize(vs->ds, 640, 400);
-
- vs->as.freq = 44100;
- vs->as.nchannels = 2;
- vs->as.fmt = AUD_FMT_S16;
- vs->as.endianness = 0;
++ dcl->dpy_update = vnc_dpy_update;
++ dcl->dpy_resize = vnc_dpy_resize;
++ dcl->dpy_setdata = vnc_dpy_setdata;
++ dcl->dpy_refresh = NULL;
++ register_displaychangelistener(ds, dcl);
}
#ifdef CONFIG_VNC_TLS
--- /dev/null
+/*
+ * We #include this from vl.c.
+ *
+ * This is a bit yucky, but it means that the line numbers and other
+ * textual differences in vl.c remain small.
+ */
+/* There is no need for multiple-inclusion protection since
+ * there is only one place where this file is included. */
+
+#include "qemu-xen.h"
+
+/* We use simpler state save/load functions for Xen */
+
+void do_savevm(const char *name)
+{
+ QEMUFile *f;
+ int saved_vm_running, ret;
+
+ f = qemu_fopen(name, "wb");
+
+ /* ??? Should this occur after vm_stop? */
+ qemu_aio_flush();
+
+ saved_vm_running = vm_running;
+ vm_stop(0);
+
+ if (!f) {
+ fprintf(logfile, "Failed to open savevm file '%s'\n", name);
+ goto the_end;
+ }
+
+ ret = qemu_savevm_state(f);
+ qemu_fclose(f);
+
+ if (ret < 0)
+ fprintf(logfile, "Error %d while writing VM to savevm file '%s'\n",
+ ret, name);
+
+ the_end:
+ if (saved_vm_running)
+ vm_start();
+
+ return;
+}
+void do_loadvm(const char *name)
+{
+ QEMUFile *f;
+ int saved_vm_running, ret;
+
+ /* Flush all IO requests so they don't interfere with the new state. */
+ qemu_aio_flush();
+
+ saved_vm_running = vm_running;
+ vm_stop(0);
+
+ /* restore the VM state */
+ f = qemu_fopen(name, "rb");
+ if (!f) {
+ fprintf(logfile, "Could not open VM state file\n");
+ goto the_end;
+ }
+
+ ret = qemu_loadvm_state(f);
+ qemu_fclose(f);
+ if (ret < 0) {
+ fprintf(logfile, "Error %d while loading savevm file '%s'\n",
+ ret, name);
+ goto the_end;
+ }
+
+#if 0
+ /* del tmp file */
+ if (unlink(name) == -1)
+ fprintf(stderr, "delete tmp qemu state file failed.\n");
+#endif
+
+
+ the_end:
+ if (saved_vm_running)
+ vm_start();
+}
+
+struct qemu_alarm_timer;
+static int unix_start_timer(struct qemu_alarm_timer *t) { return 0; }
+static void unix_stop_timer(struct qemu_alarm_timer *t) { }
+
+#ifdef CONFIG_STUBDOM
+#include <netfront.h>
+static int tap_open(char *ifname, int ifname_size)
+{
+ char nodename[64];
+ static int num = 1; // 0 is for our own TCP/IP networking
+ snprintf(nodename, sizeof(nodename), "device/vif/%d", num++);
+ return netfront_tap_open(nodename);
+}
+
+#undef DEFAULT_NETWORK_SCRIPT
+#define DEFAULT_NETWORK_SCRIPT ""
+#undef DEFAULT_NETWORK_DOWN_SCRIPT
+#define DEFAULT_NETWORK_DOWN_SCRIPT ""
+#endif
+
++#ifdef CONFIG_PASSTHROUGH
++void do_pci_del(char *devname)
++{
++ int pci_slot;
++ pci_slot = bdf_to_slot(devname);
++
++ acpi_php_del(pci_slot);
++}
++
++void do_pci_add(char *devname)
++{
++ int pci_slot;
++
++ pci_slot = insert_to_pci_slot(devname);
++
++ acpi_php_add(pci_slot);
++}
++
++static int pci_emulation_add(char *config_text)
++{
++ PCI_EMULATION_INFO *new;
++ if ((new = qemu_mallocz(sizeof(PCI_EMULATION_INFO))) == NULL) {
++ return -1;
++ }
++ parse_pci_emulation_info(config_text, new);
++ new->next = PciEmulationInfoHead;
++ PciEmulationInfoHead = new;
++ return 0;
++}
++
++#endif