diff options
Diffstat (limited to 'hw')
92 files changed, 8216 insertions, 1078 deletions
diff --git a/hw/Makefile.objs b/hw/Makefile.objs index eb7eb31a19..d0b2ecb97f 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -98,6 +98,7 @@ common-obj-$(CONFIG_PL110) += pl110.o common-obj-$(CONFIG_PL181) += pl181.o common-obj-$(CONFIG_PL190) += pl190.o common-obj-$(CONFIG_PL310) += arm_l2x0.o +common-obj-$(CONFIG_PL330) += pl330.o common-obj-$(CONFIG_VERSATILE_PCI) += versatile_pci.o common-obj-$(CONFIG_VERSATILE_I2C) += versatile_i2c.o common-obj-$(CONFIG_CADENCE) += cadence_uart.o @@ -118,6 +119,8 @@ common-obj-$(CONFIG_PCNET_PCI) += pcnet-pci.o common-obj-$(CONFIG_PCNET_COMMON) += pcnet.o common-obj-$(CONFIG_E1000_PCI) += e1000.o common-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o +common-obj-$(CONFIG_VMXNET3_PCI) += vmxnet_tx_pkt.o vmxnet_rx_pkt.o +common-obj-$(CONFIG_VMXNET3_PCI) += vmxnet3.o common-obj-$(CONFIG_SMC91C111) += smc91c111.o common-obj-$(CONFIG_LAN9118) += lan9118.o diff --git a/hw/arm-misc.h b/hw/arm-misc.h index cba7553039..7b2b02daaf 100644 --- a/hw/arm-misc.h +++ b/hw/arm-misc.h @@ -14,7 +14,7 @@ #include "exec/memory.h" #include "hw/irq.h" -/* The CPU is also modeled as an interrupt controller. */ +/* The CPU is also modelled as an interrupt controller. */ #define ARM_PIC_CPU_IRQ 0 #define ARM_PIC_CPU_FIQ 1 qemu_irq *arm_pic_init_cpu(ARMCPU *cpu); diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index a37dbd7961..edd528255f 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -462,7 +462,7 @@ typedef struct musicpal_lcd_state { uint32_t irqctrl; uint32_t page; uint32_t page_off; - DisplayState *ds; + QemuConsole *con; uint8_t video_ram[128*64/8]; } musicpal_lcd_state; @@ -483,7 +483,8 @@ static inline void glue(set_lcd_pixel, depth) \ (musicpal_lcd_state *s, int x, int y, type col) \ { \ int dx, dy; \ - type *pixel = &((type *) ds_get_data(s->ds))[(y * 128 * 3 + x) * 3]; \ + DisplaySurface *surface = qemu_console_surface(s->con); \ + type *pixel = &((type *) surface_data(surface))[(y * 128 * 3 + x) * 3]; \ \ for (dy = 0; dy < 3; dy++, pixel += 127 * 3) \ for (dx = 0; dx < 3; dx++, pixel++) \ @@ -496,9 +497,10 @@ SET_LCD_PIXEL(32, uint32_t) static void lcd_refresh(void *opaque) { musicpal_lcd_state *s = opaque; + DisplaySurface *surface = qemu_console_surface(s->con); int x, y, col; - switch (ds_get_bits_per_pixel(s->ds)) { + switch (surface_bits_per_pixel(surface)) { case 0: return; #define LCD_REFRESH(depth, func) \ @@ -518,14 +520,14 @@ static void lcd_refresh(void *opaque) break; LCD_REFRESH(8, rgb_to_pixel8) LCD_REFRESH(16, rgb_to_pixel16) - LCD_REFRESH(32, (is_surface_bgr(s->ds->surface) ? + LCD_REFRESH(32, (is_surface_bgr(surface) ? rgb_to_pixel32bgr : rgb_to_pixel32)) default: hw_error("unsupported colour depth %i\n", - ds_get_bits_per_pixel(s->ds)); + surface_bits_per_pixel(surface)); } - dpy_gfx_update(s->ds, 0, 0, 128*3, 64*3); + dpy_gfx_update(s->con, 0, 0, 128*3, 64*3); } static void lcd_invalidate(void *opaque) @@ -609,9 +611,9 @@ static int musicpal_lcd_init(SysBusDevice *dev) "musicpal-lcd", MP_LCD_SIZE); sysbus_init_mmio(dev, &s->iomem); - s->ds = graphic_console_init(lcd_refresh, lcd_invalidate, - NULL, NULL, s); - qemu_console_resize(s->ds, 128*3, 64*3); + s->con = graphic_console_init(lcd_refresh, lcd_invalidate, + NULL, NULL, s); + qemu_console_resize(s->con, 128*3, 64*3); qdev_init_gpio_in(&dev->qdev, musicpal_lcd_gpio_brigthness_in, 3); diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c index c5bf9f95b3..6747c1c547 100644 --- a/hw/arm/nseries.c +++ b/hw/arm/nseries.c @@ -1290,7 +1290,6 @@ static void n8x0_init(QEMUMachineInitArgs *args, MemoryRegion *sysmem = get_system_memory(); struct n800_s *s = (struct n800_s *) g_malloc0(sizeof(*s)); int sdram_size = binfo->ram_size; - DisplayState *ds; s->mpu = omap2420_mpu_init(sysmem, sdram_size, args->cpu_model); @@ -1370,12 +1369,6 @@ static void n8x0_init(QEMUMachineInitArgs *args, n800_setup_nolo_tags(nolo_tags); cpu_physical_memory_write(OMAP2_SRAM_BASE, nolo_tags, 0x10000); } - /* FIXME: We shouldn't really be doing this here. The LCD controller - will set the size once configured, so this just sets an initial - size until the guest activates the display. */ - ds = get_displaystate(); - ds->surface = qemu_resize_displaysurface(ds, 800, 480); - dpy_gfx_resize(ds); } static struct arm_boot_info n800_binfo = { diff --git a/hw/arm/palm.c b/hw/arm/palm.c index 91bc74af24..baeb585067 100644 --- a/hw/arm/palm.c +++ b/hw/arm/palm.c @@ -205,7 +205,6 @@ static void palmte_init(QEMUMachineInitArgs *args) static uint32_t cs2val = 0x0000e1a0; static uint32_t cs3val = 0xe1a0e1a0; int rom_size, rom_loaded = 0; - DisplayState *ds = get_displaystate(); MemoryRegion *flash = g_new(MemoryRegion, 1); MemoryRegion *cs = g_new(MemoryRegion, 4); @@ -268,12 +267,6 @@ static void palmte_init(QEMUMachineInitArgs *args) palmte_binfo.initrd_filename = initrd_filename; arm_load_kernel(mpu->cpu, &palmte_binfo); } - - /* FIXME: We shouldn't really be doing this here. The LCD controller - will set the size once configured, so this just sets an initial - size until the guest activates the display. */ - ds->surface = qemu_resize_displaysurface(ds, 320, 320); - dpy_gfx_resize(ds); } static QEMUMachine palmte_machine = { diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index 02922c38b3..2e1a5d0e5b 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -147,19 +147,24 @@ typedef struct VEDBoardInfo VEDBoardInfo; typedef void DBoardInitFn(const VEDBoardInfo *daughterboard, ram_addr_t ram_size, const char *cpu_model, - qemu_irq *pic, uint32_t *proc_id); + qemu_irq *pic); struct VEDBoardInfo { const hwaddr *motherboard_map; hwaddr loader_start; const hwaddr gic_cpu_if_addr; + uint32_t proc_id; + uint32_t num_voltage_sensors; + const uint32_t *voltages; + uint32_t num_clocks; + const uint32_t *clocks; DBoardInitFn *init; }; static void a9_daughterboard_init(const VEDBoardInfo *daughterboard, ram_addr_t ram_size, const char *cpu_model, - qemu_irq *pic, uint32_t *proc_id) + qemu_irq *pic) { MemoryRegion *sysmem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); @@ -175,8 +180,6 @@ static void a9_daughterboard_init(const VEDBoardInfo *daughterboard, cpu_model = "cortex-a9"; } - *proc_id = 0x0c000191; - for (n = 0; n < smp_cpus; n++) { ARMCPU *cpu = cpu_arm_init(cpu_model); if (!cpu) { @@ -247,17 +250,41 @@ static void a9_daughterboard_init(const VEDBoardInfo *daughterboard, sysbus_create_varargs("l2x0", 0x1e00a000, NULL); } +/* Voltage values for SYS_CFG_VOLT daughterboard registers; + * values are in microvolts. + */ +static const uint32_t a9_voltages[] = { + 1000000, /* VD10 : 1.0V : SoC internal logic voltage */ + 1000000, /* VD10_S2 : 1.0V : PL310, L2 cache, RAM, non-PL310 logic */ + 1000000, /* VD10_S3 : 1.0V : Cortex-A9, cores, MPEs, SCU, PL310 logic */ + 1800000, /* VCC1V8 : 1.8V : DDR2 SDRAM, test chip DDR2 I/O supply */ + 900000, /* DDR2VTT : 0.9V : DDR2 SDRAM VTT termination voltage */ + 3300000, /* VCC3V3 : 3.3V : local board supply for misc external logic */ +}; + +/* Reset values for daughterboard oscillators (in Hz) */ +static const uint32_t a9_clocks[] = { + 45000000, /* AMBA AXI ACLK: 45MHz */ + 23750000, /* daughterboard CLCD clock: 23.75MHz */ + 66670000, /* Test chip reference clock: 66.67MHz */ +}; + static const VEDBoardInfo a9_daughterboard = { .motherboard_map = motherboard_legacy_map, .loader_start = 0x60000000, .gic_cpu_if_addr = 0x1e000100, + .proc_id = 0x0c000191, + .num_voltage_sensors = ARRAY_SIZE(a9_voltages), + .voltages = a9_voltages, + .num_clocks = ARRAY_SIZE(a9_clocks), + .clocks = a9_clocks, .init = a9_daughterboard_init, }; static void a15_daughterboard_init(const VEDBoardInfo *daughterboard, ram_addr_t ram_size, const char *cpu_model, - qemu_irq *pic, uint32_t *proc_id) + qemu_irq *pic) { int n; MemoryRegion *sysmem = get_system_memory(); @@ -271,8 +298,6 @@ static void a15_daughterboard_init(const VEDBoardInfo *daughterboard, cpu_model = "cortex-a15"; } - *proc_id = 0x14000237; - for (n = 0; n < smp_cpus; n++) { ARMCPU *cpu; qemu_irq *irqp; @@ -340,10 +365,31 @@ static void a15_daughterboard_init(const VEDBoardInfo *daughterboard, /* 0x7ffd0000: PL354 static memory controller: not modelled */ } +static const uint32_t a15_voltages[] = { + 900000, /* Vcore: 0.9V : CPU core voltage */ +}; + +static const uint32_t a15_clocks[] = { + 60000000, /* OSCCLK0: 60MHz : CPU_CLK reference */ + 0, /* OSCCLK1: reserved */ + 0, /* OSCCLK2: reserved */ + 0, /* OSCCLK3: reserved */ + 40000000, /* OSCCLK4: 40MHz : external AXI master clock */ + 23750000, /* OSCCLK5: 23.75MHz : HDLCD PLL reference */ + 50000000, /* OSCCLK6: 50MHz : static memory controller clock */ + 60000000, /* OSCCLK7: 60MHz : SYSCLK reference */ + 40000000, /* OSCCLK8: 40MHz : DDR2 PLL reference */ +}; + static const VEDBoardInfo a15_daughterboard = { .motherboard_map = motherboard_aseries_map, .loader_start = 0x80000000, .gic_cpu_if_addr = 0x2c002000, + .proc_id = 0x14000237, + .num_voltage_sensors = ARRAY_SIZE(a15_voltages), + .voltages = a15_voltages, + .num_clocks = ARRAY_SIZE(a15_clocks), + .clocks = a15_clocks, .init = a15_daughterboard_init, }; @@ -352,7 +398,6 @@ static void vexpress_common_init(const VEDBoardInfo *daughterboard, { DeviceState *dev, *sysctl, *pl041; qemu_irq pic[64]; - uint32_t proc_id; uint32_t sys_id; DriveInfo *dinfo; ram_addr_t vram_size, sram_size; @@ -360,9 +405,9 @@ static void vexpress_common_init(const VEDBoardInfo *daughterboard, MemoryRegion *vram = g_new(MemoryRegion, 1); MemoryRegion *sram = g_new(MemoryRegion, 1); const hwaddr *map = daughterboard->motherboard_map; + int i; - daughterboard->init(daughterboard, args->ram_size, args->cpu_model, - pic, &proc_id); + daughterboard->init(daughterboard, args->ram_size, args->cpu_model, pic); /* Motherboard peripherals: the wiring is the same but the * addresses vary between the legacy and A-Series memory maps. @@ -372,7 +417,21 @@ static void vexpress_common_init(const VEDBoardInfo *daughterboard, sysctl = qdev_create(NULL, "realview_sysctl"); qdev_prop_set_uint32(sysctl, "sys_id", sys_id); - qdev_prop_set_uint32(sysctl, "proc_id", proc_id); + qdev_prop_set_uint32(sysctl, "proc_id", daughterboard->proc_id); + qdev_prop_set_uint32(sysctl, "len-db-voltage", + daughterboard->num_voltage_sensors); + for (i = 0; i < daughterboard->num_voltage_sensors; i++) { + char *propname = g_strdup_printf("db-voltage[%d]", i); + qdev_prop_set_uint32(sysctl, propname, daughterboard->voltages[i]); + g_free(propname); + } + qdev_prop_set_uint32(sysctl, "len-db-clock", + daughterboard->num_clocks); + for (i = 0; i < daughterboard->num_clocks; i++) { + char *propname = g_strdup_printf("db-clock[%d]", i); + qdev_prop_set_uint32(sysctl, propname, daughterboard->clocks[i]); + g_free(propname); + } qdev_init_nofail(sysctl); sysbus_mmio_map(SYS_BUS_DEVICE(sysctl), 0, map[VE_SYSREGS]); diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index f78c47e43e..6f362865f9 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -35,6 +35,10 @@ #define IRQ_OFFSET 32 /* pic interrupts start from index 32 */ +static const int dma_irqs[8] = { + 46, 47, 48, 49, 72, 73, 74, 75 +}; + static struct arm_boot_info zynq_binfo = {}; static void gem_init(NICInfo *nd, uint32_t base, qemu_irq irq) @@ -196,6 +200,26 @@ static void zynq_init(QEMUMachineInitArgs *args) sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xE0101000); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[79-IRQ_OFFSET]); + dev = qdev_create(NULL, "pl330"); + qdev_prop_set_uint8(dev, "num_chnls", 8); + qdev_prop_set_uint8(dev, "num_periph_req", 4); + qdev_prop_set_uint8(dev, "num_events", 16); + + qdev_prop_set_uint8(dev, "data_width", 64); + qdev_prop_set_uint8(dev, "wr_cap", 8); + qdev_prop_set_uint8(dev, "wr_q_dep", 16); + qdev_prop_set_uint8(dev, "rd_cap", 8); + qdev_prop_set_uint8(dev, "rd_q_dep", 16); + qdev_prop_set_uint16(dev, "data_buffer_dep", 256); + + qdev_init_nofail(dev); + busdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(busdev, 0, 0xF8003000); + sysbus_connect_irq(busdev, 0, pic[45-IRQ_OFFSET]); /* abort irq line */ + for (n = 0; n < 8; ++n) { /* event irqs */ + sysbus_connect_irq(busdev, n + 1, pic[dma_irqs[n] - IRQ_OFFSET]); + } + zynq_binfo.ram_size = ram_size; zynq_binfo.kernel_filename = kernel_filename; zynq_binfo.kernel_cmdline = kernel_cmdline; diff --git a/hw/arm_sysctl.c b/hw/arm_sysctl.c index a46f8d450e..25fc6eac94 100644 --- a/hw/arm_sysctl.c +++ b/hw/arm_sysctl.c @@ -9,6 +9,7 @@ #include "hw/hw.h" #include "qemu/timer.h" +#include "qemu/bitops.h" #include "hw/sysbus.h" #include "hw/primecell.h" #include "sysemu/sysemu.h" @@ -34,11 +35,17 @@ typedef struct { uint32_t sys_cfgctrl; uint32_t sys_cfgstat; uint32_t sys_clcd; + uint32_t mb_clock[6]; + uint32_t *db_clock; + uint32_t db_num_vsensors; + uint32_t *db_voltage; + uint32_t db_num_clocks; + uint32_t *db_clock_reset; } arm_sysctl_state; static const VMStateDescription vmstate_arm_sysctl = { .name = "realview_sysctl", - .version_id = 3, + .version_id = 4, .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_UINT32(leds, arm_sysctl_state), @@ -53,6 +60,9 @@ static const VMStateDescription vmstate_arm_sysctl = { VMSTATE_UINT32_V(sys_cfgctrl, arm_sysctl_state, 2), VMSTATE_UINT32_V(sys_cfgstat, arm_sysctl_state, 2), VMSTATE_UINT32_V(sys_clcd, arm_sysctl_state, 3), + VMSTATE_UINT32_ARRAY_V(mb_clock, arm_sysctl_state, 6, 4), + VMSTATE_VARRAY_UINT32(db_clock, arm_sysctl_state, db_num_clocks, + 4, vmstate_info_uint32, uint32_t), VMSTATE_END_OF_LIST() } }; @@ -76,6 +86,7 @@ static int board_id(arm_sysctl_state *s) static void arm_sysctl_reset(DeviceState *d) { arm_sysctl_state *s = FROM_SYSBUS(arm_sysctl_state, SYS_BUS_DEVICE(d)); + int i; s->leds = 0; s->lockval = 0; @@ -83,6 +94,17 @@ static void arm_sysctl_reset(DeviceState *d) s->cfgdata2 = 0; s->flags = 0; s->resetlevel = 0; + /* Motherboard oscillators (in Hz) */ + s->mb_clock[0] = 50000000; /* Static memory clock: 50MHz */ + s->mb_clock[1] = 23750000; /* motherboard CLCD clock: 23.75MHz */ + s->mb_clock[2] = 24000000; /* IO FPGA peripheral clock: 24MHz */ + s->mb_clock[3] = 24000000; /* IO FPGA reserved clock: 24MHz */ + s->mb_clock[4] = 24000000; /* System bus global clock: 24MHz */ + s->mb_clock[5] = 24000000; /* IO FPGA reserved clock: 24MHz */ + /* Daughterboard oscillators: reset from property values */ + for (i = 0; i < s->db_num_clocks; i++) { + s->db_clock[i] = s->db_clock_reset[i]; + } if (board_id(s) == BOARD_ID_VEXPRESS) { /* On VExpress this register will RAZ/WI */ s->sys_clcd = 0; @@ -191,6 +213,166 @@ static uint64_t arm_sysctl_read(void *opaque, hwaddr offset, } } +/* SYS_CFGCTRL functions */ +#define SYS_CFG_OSC 1 +#define SYS_CFG_VOLT 2 +#define SYS_CFG_AMP 3 +#define SYS_CFG_TEMP 4 +#define SYS_CFG_RESET 5 +#define SYS_CFG_SCC 6 +#define SYS_CFG_MUXFPGA 7 +#define SYS_CFG_SHUTDOWN 8 +#define SYS_CFG_REBOOT 9 +#define SYS_CFG_DVIMODE 11 +#define SYS_CFG_POWER 12 +#define SYS_CFG_ENERGY 13 + +/* SYS_CFGCTRL site field values */ +#define SYS_CFG_SITE_MB 0 +#define SYS_CFG_SITE_DB1 1 +#define SYS_CFG_SITE_DB2 2 + +/** + * vexpress_cfgctrl_read: + * @s: arm_sysctl_state pointer + * @dcc, @function, @site, @position, @device: split out values from + * SYS_CFGCTRL register + * @val: pointer to where to put the read data on success + * + * Handle a VExpress SYS_CFGCTRL register read. On success, return true and + * write the read value to *val. On failure, return false (and val may + * or may not be written to). + */ +static bool vexpress_cfgctrl_read(arm_sysctl_state *s, unsigned int dcc, + unsigned int function, unsigned int site, + unsigned int position, unsigned int device, + uint32_t *val) +{ + /* We don't support anything other than DCC 0, board stack position 0 + * or sites other than motherboard/daughterboard: + */ + if (dcc != 0 || position != 0 || + (site != SYS_CFG_SITE_MB && site != SYS_CFG_SITE_DB1)) { + goto cfgctrl_unimp; + } + + switch (function) { + case SYS_CFG_VOLT: + if (site == SYS_CFG_SITE_DB1 && device < s->db_num_vsensors) { + *val = s->db_voltage[device]; + return true; + } + if (site == SYS_CFG_SITE_MB && device == 0) { + /* There is only one motherboard voltage sensor: + * VIO : 3.3V : bus voltage between mother and daughterboard + */ + *val = 3300000; + return true; + } + break; + case SYS_CFG_OSC: + if (site == SYS_CFG_SITE_MB && device < sizeof(s->mb_clock)) { + /* motherboard clock */ + *val = s->mb_clock[device]; + return true; + } + if (site == SYS_CFG_SITE_DB1 && device < s->db_num_clocks) { + /* daughterboard clock */ + *val = s->db_clock[device]; + return true; + } + break; + default: + break; + } + +cfgctrl_unimp: + qemu_log_mask(LOG_UNIMP, + "arm_sysctl: Unimplemented SYS_CFGCTRL read of function " + "0x%x DCC 0x%x site 0x%x position 0x%x device 0x%x\n", + function, dcc, site, position, device); + return false; +} + +/** + * vexpress_cfgctrl_write: + * @s: arm_sysctl_state pointer + * @dcc, @function, @site, @position, @device: split out values from + * SYS_CFGCTRL register + * @val: data to write + * + * Handle a VExpress SYS_CFGCTRL register write. On success, return true. + * On failure, return false. + */ +static bool vexpress_cfgctrl_write(arm_sysctl_state *s, unsigned int dcc, + unsigned int function, unsigned int site, + unsigned int position, unsigned int device, + uint32_t val) +{ + /* We don't support anything other than DCC 0, board stack position 0 + * or sites other than motherboard/daughterboard: + */ + if (dcc != 0 || position != 0 || + (site != SYS_CFG_SITE_MB && site != SYS_CFG_SITE_DB1)) { + goto cfgctrl_unimp; + } + + switch (function) { + case SYS_CFG_OSC: + if (site == SYS_CFG_SITE_MB && device < sizeof(s->mb_clock)) { + /* motherboard clock */ + s->mb_clock[device] = val; + return true; + } + if (site == SYS_CFG_SITE_DB1 && device < s->db_num_clocks) { + /* daughterboard clock */ + s->db_clock[device] = val; + return true; + } + break; + case SYS_CFG_MUXFPGA: + if (site == SYS_CFG_SITE_MB && device == 0) { + /* Select whether video output comes from motherboard + * or daughterboard: log and ignore as QEMU doesn't + * support this. + */ + qemu_log_mask(LOG_UNIMP, "arm_sysctl: selection of video output " + "not supported, ignoring\n"); + return true; + } + break; + case SYS_CFG_SHUTDOWN: + if (site == SYS_CFG_SITE_MB && device == 0) { + qemu_system_shutdown_request(); + return true; + } + break; + case SYS_CFG_REBOOT: + if (site == SYS_CFG_SITE_MB && device == 0) { + qemu_system_reset_request(); + return true; + } + break; + case SYS_CFG_DVIMODE: + if (site == SYS_CFG_SITE_MB && device == 0) { + /* Selecting DVI mode is meaningless for QEMU: we will + * always display the output correctly according to the + * pixel height/width programmed into the CLCD controller. + */ + return true; + } + default: + break; + } + +cfgctrl_unimp: + qemu_log_mask(LOG_UNIMP, + "arm_sysctl: Unimplemented SYS_CFGCTRL write of function " + "0x%x DCC 0x%x site 0x%x position 0x%x device 0x%x\n", + function, dcc, site, position, device); + return false; +} + static void arm_sysctl_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) { @@ -322,17 +504,33 @@ static void arm_sysctl_write(void *opaque, hwaddr offset, if (board_id(s) != BOARD_ID_VEXPRESS) { goto bad_reg; } - s->sys_cfgctrl = val & ~(3 << 18); - s->sys_cfgstat = 1; /* complete */ - switch (s->sys_cfgctrl) { - case 0xc0800000: /* SYS_CFG_SHUTDOWN to motherboard */ - qemu_system_shutdown_request(); - break; - case 0xc0900000: /* SYS_CFG_REBOOT to motherboard */ - qemu_system_reset_request(); - break; - default: - s->sys_cfgstat |= 2; /* error */ + /* Undefined bits [19:18] are RAZ/WI, and writing to + * the start bit just triggers the action; it always reads + * as zero. + */ + s->sys_cfgctrl = val & ~((3 << 18) | (1 << 31)); + if (val & (1 << 31)) { + /* Start bit set -- actually do something */ + unsigned int dcc = extract32(s->sys_cfgctrl, 26, 4); + unsigned int function = extract32(s->sys_cfgctrl, 20, 6); + unsigned int site = extract32(s->sys_cfgctrl, 16, 2); + unsigned int position = extract32(s->sys_cfgctrl, 12, 4); + unsigned int device = extract32(s->sys_cfgctrl, 0, 12); + s->sys_cfgstat = 1; /* complete */ + if (s->sys_cfgctrl & (1 << 30)) { + if (!vexpress_cfgctrl_write(s, dcc, function, site, position, + device, s->sys_cfgdata)) { + s->sys_cfgstat |= 2; /* error */ + } + } else { + uint32_t val; + if (!vexpress_cfgctrl_read(s, dcc, function, site, position, + device, &val)) { + s->sys_cfgstat |= 2; /* error */ + } else { + s->sys_cfgdata = val; + } + } } s->sys_cfgctrl &= ~(1 << 31); return; @@ -385,29 +583,50 @@ static void arm_sysctl_gpio_set(void *opaque, int line, int level) } } -static int arm_sysctl_init(SysBusDevice *dev) +static void arm_sysctl_init(Object *obj) { - arm_sysctl_state *s = FROM_SYSBUS(arm_sysctl_state, dev); + DeviceState *dev = DEVICE(obj); + SysBusDevice *sd = SYS_BUS_DEVICE(obj); + arm_sysctl_state *s = FROM_SYSBUS(arm_sysctl_state, sd); memory_region_init_io(&s->iomem, &arm_sysctl_ops, s, "arm-sysctl", 0x1000); - sysbus_init_mmio(dev, &s->iomem); - qdev_init_gpio_in(&s->busdev.qdev, arm_sysctl_gpio_set, 2); - qdev_init_gpio_out(&s->busdev.qdev, &s->pl110_mux_ctrl, 1); - return 0; + sysbus_init_mmio(sd, &s->iomem); + qdev_init_gpio_in(dev, arm_sysctl_gpio_set, 2); + qdev_init_gpio_out(dev, &s->pl110_mux_ctrl, 1); +} + +static void arm_sysctl_realize(DeviceState *d, Error **errp) +{ + arm_sysctl_state *s = FROM_SYSBUS(arm_sysctl_state, SYS_BUS_DEVICE(d)); + s->db_clock = g_new0(uint32_t, s->db_num_clocks); +} + +static void arm_sysctl_finalize(Object *obj) +{ + SysBusDevice *dev = SYS_BUS_DEVICE(obj); + arm_sysctl_state *s = FROM_SYSBUS(arm_sysctl_state, dev); + g_free(s->db_voltage); + g_free(s->db_clock); + g_free(s->db_clock_reset); } static Property arm_sysctl_properties[] = { DEFINE_PROP_UINT32("sys_id", arm_sysctl_state, sys_id, 0), DEFINE_PROP_UINT32("proc_id", arm_sysctl_state, proc_id, 0), + /* Daughterboard power supply voltages (as reported via SYS_CFG) */ + DEFINE_PROP_ARRAY("db-voltage", arm_sysctl_state, db_num_vsensors, + db_voltage, qdev_prop_uint32, uint32_t), + /* Daughterboard clock reset values (as reported via SYS_CFG) */ + DEFINE_PROP_ARRAY("db-clock", arm_sysctl_state, db_num_clocks, + db_clock_reset, qdev_prop_uint32, uint32_t), DEFINE_PROP_END_OF_LIST(), }; static void arm_sysctl_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = arm_sysctl_init; + dc->realize = arm_sysctl_realize; dc->reset = arm_sysctl_reset; dc->vmsd = &vmstate_arm_sysctl; dc->props = arm_sysctl_properties; @@ -417,6 +636,8 @@ static const TypeInfo arm_sysctl_info = { .name = "realview_sysctl", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(arm_sysctl_state), + .instance_init = arm_sysctl_init, + .instance_finalize = arm_sysctl_finalize, .class_init = arm_sysctl_class_init, }; diff --git a/hw/blizzard.c b/hw/blizzard.c index 805f4d5558..020d3de431 100644 --- a/hw/blizzard.c +++ b/hw/blizzard.c @@ -69,7 +69,7 @@ typedef struct { uint8_t effect; uint8_t iformat; uint8_t source; - DisplayState *state; + QemuConsole *con; blizzard_fn_t *line_fn_tab[2]; void *fb; @@ -144,6 +144,7 @@ static inline void blizzard_rgb2yuv(int r, int g, int b, static void blizzard_window(BlizzardState *s) { + DisplaySurface *surface = qemu_console_surface(s->con); uint8_t *src, *dst; int bypp[2]; int bypl[3]; @@ -162,7 +163,7 @@ static void blizzard_window(BlizzardState *s) s->my[1] = s->data.y + s->data.dy; bypp[0] = s->bpp; - bypp[1] = (ds_get_bits_per_pixel(s->state) + 7) >> 3; + bypp[1] = surface_bytes_per_pixel(surface); bypl[0] = bypp[0] * s->data.pitch; bypl[1] = bypp[1] * s->x; bypl[2] = bypp[0] * s->data.dx; @@ -883,23 +884,25 @@ void s1d13745_write_block(void *opaque, int dc, static void blizzard_update_display(void *opaque) { BlizzardState *s = (BlizzardState *) opaque; + DisplaySurface *surface = qemu_console_surface(s->con); int y, bypp, bypl, bwidth; uint8_t *src, *dst; if (!s->enable) return; - if (s->x != ds_get_width(s->state) || s->y != ds_get_height(s->state)) { + if (s->x != surface_width(surface) || s->y != surface_height(surface)) { s->invalidate = 1; - qemu_console_resize(s->state, s->x, s->y); + qemu_console_resize(s->con, s->x, s->y); + surface = qemu_console_surface(s->con); } if (s->invalidate) { s->invalidate = 0; if (s->blank) { - bypp = (ds_get_bits_per_pixel(s->state) + 7) >> 3; - memset(ds_get_data(s->state), 0, bypp * s->x * s->y); + bypp = surface_bytes_per_pixel(surface); + memset(surface_data(surface), 0, bypp * s->x * s->y); return; } @@ -912,16 +915,16 @@ static void blizzard_update_display(void *opaque) if (s->mx[1] <= s->mx[0]) return; - bypp = (ds_get_bits_per_pixel(s->state) + 7) >> 3; + bypp = surface_bytes_per_pixel(surface); bypl = bypp * s->x; bwidth = bypp * (s->mx[1] - s->mx[0]); y = s->my[0]; src = s->fb + bypl * y + bypp * s->mx[0]; - dst = ds_get_data(s->state) + bypl * y + bypp * s->mx[0]; + dst = surface_data(surface) + bypl * y + bypp * s->mx[0]; for (; y < s->my[1]; y ++, src += bypl, dst += bypl) memcpy(dst, src, bwidth); - dpy_gfx_update(s->state, s->mx[0], s->my[0], + dpy_gfx_update(s->con, s->mx[0], s->my[0], s->mx[1] - s->mx[0], y - s->my[0]); s->mx[0] = s->x; @@ -934,10 +937,12 @@ static void blizzard_screen_dump(void *opaque, const char *filename, bool cswitch, Error **errp) { BlizzardState *s = (BlizzardState *) opaque; + DisplaySurface *surface = qemu_console_surface(s->con); blizzard_update_display(opaque); - if (s && ds_get_data(s->state)) - ppm_save(filename, s->state->surface, errp); + if (s && surface_data(surface)) { + ppm_save(filename, surface, errp); + } } #define DEPTH 8 @@ -954,14 +959,16 @@ static void blizzard_screen_dump(void *opaque, const char *filename, void *s1d13745_init(qemu_irq gpio_int) { BlizzardState *s = (BlizzardState *) g_malloc0(sizeof(*s)); + DisplaySurface *surface; s->fb = g_malloc(0x180000); - s->state = graphic_console_init(blizzard_update_display, - blizzard_invalidate_display, - blizzard_screen_dump, NULL, s); + s->con = graphic_console_init(blizzard_update_display, + blizzard_invalidate_display, + blizzard_screen_dump, NULL, s); + surface = qemu_console_surface(s->con); - switch (ds_get_bits_per_pixel(s->state)) { + switch (surface_bits_per_pixel(surface)) { case 0: s->line_fn_tab[0] = s->line_fn_tab[1] = g_malloc0(sizeof(blizzard_fn_t) * 0x10); diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c index 7babcb67c8..7a4d63436e 100644 --- a/hw/cirrus_vga.c +++ b/hw/cirrus_vga.c @@ -729,11 +729,12 @@ static void cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h) s->cirrus_blt_dstpitch, s->cirrus_blt_srcpitch, s->cirrus_blt_width, s->cirrus_blt_height); - if (notify) - qemu_console_copy(s->vga.ds, + if (notify) { + qemu_console_copy(s->vga.con, sx, sy, dx, dy, s->cirrus_blt_width / depth, s->cirrus_blt_height); + } /* we don't have to notify the display that this portion has changed since qemu_console_copy implies this */ @@ -2176,6 +2177,7 @@ static void cirrus_cursor_invalidate(VGACommonState *s1) static void cirrus_cursor_draw_line(VGACommonState *s1, uint8_t *d1, int scr_y) { CirrusVGAState *s = container_of(s1, CirrusVGAState, vga); + DisplaySurface *surface = qemu_console_surface(s->vga.con); int w, h, bpp, x1, x2, poffset; unsigned int color0, color1; const uint8_t *palette, *src; @@ -2228,9 +2230,9 @@ static void cirrus_cursor_draw_line(VGACommonState *s1, uint8_t *d1, int scr_y) color1 = s->vga.rgb_to_pixel(c6_to_8(palette[0xf * 3]), c6_to_8(palette[0xf * 3 + 1]), c6_to_8(palette[0xf * 3 + 2])); - bpp = ((ds_get_bits_per_pixel(s->vga.ds) + 7) >> 3); + bpp = surface_bytes_per_pixel(surface); d1 += x1 * bpp; - switch(ds_get_bits_per_pixel(s->vga.ds)) { + switch (surface_bits_per_pixel(surface)) { default: break; case 8: @@ -2908,9 +2910,9 @@ static int vga_initfn(ISADevice *dev) vga_common_init(s); cirrus_init_common(&d->cirrus_vga, CIRRUS_ID_CLGD5430, 0, isa_address_space(dev), isa_address_space_io(dev)); - s->ds = graphic_console_init(s->update, s->invalidate, - s->screen_dump, s->text_update, - s); + s->con = graphic_console_init(s->update, s->invalidate, + s->screen_dump, s->text_update, + s); rom_add_vga(VGABIOS_CIRRUS_FILENAME); /* XXX ISA-LFB support */ /* FIXME not qdev yet */ @@ -2957,9 +2959,9 @@ static int pci_cirrus_vga_initfn(PCIDevice *dev) vga_common_init(&s->vga); cirrus_init_common(s, device_id, 1, pci_address_space(dev), pci_address_space_io(dev)); - s->vga.ds = graphic_console_init(s->vga.update, s->vga.invalidate, - s->vga.screen_dump, s->vga.text_update, - &s->vga); + s->vga.con = graphic_console_init(s->vga.update, s->vga.invalidate, + s->vga.screen_dump, s->vga.text_update, + &s->vga); /* setup PCI */ diff --git a/hw/e1000.c b/hw/e1000.c index 80b6ee3c1a..3f18041b47 100644 --- a/hw/e1000.c +++ b/hw/e1000.c @@ -1378,7 +1378,7 @@ static void e1000_class_init(ObjectClass *klass, void *data) k->init = pci_e1000_init; k->exit = pci_e1000_uninit; - k->romfile = "pxe-e1000.rom"; + k->romfile = "efi-e1000.rom"; k->vendor_id = PCI_VENDOR_ID_INTEL; k->device_id = E1000_DEVID; k->revision = 0x03; diff --git a/hw/exynos4210_fimd.c b/hw/exynos4210_fimd.c index 6b31ae33b6..bf316c62ab 100644 --- a/hw/exynos4210_fimd.c +++ b/hw/exynos4210_fimd.c @@ -296,7 +296,7 @@ struct Exynos4210fimdWindow { typedef struct { SysBusDevice busdev; MemoryRegion iomem; - DisplayState *console; + QemuConsole *console; qemu_irq irq[3]; uint32_t vidcon[4]; /* Video main control registers 0-3 */ @@ -1221,16 +1221,18 @@ static void exynos4210_fimd_invalidate(void *opaque) static void exynos4210_update_resolution(Exynos4210fimdState *s) { + DisplaySurface *surface = qemu_console_surface(s->console); + /* LCD resolution is stored in VIDEO TIME CONTROL REGISTER 2 */ uint32_t width = ((s->vidtcon[2] >> FIMD_VIDTCON2_HOR_SHIFT) & FIMD_VIDTCON2_SIZE_MASK) + 1; uint32_t height = ((s->vidtcon[2] >> FIMD_VIDTCON2_VER_SHIFT) & FIMD_VIDTCON2_SIZE_MASK) + 1; - if (s->ifb == NULL || ds_get_width(s->console) != width || - ds_get_height(s->console) != height) { + if (s->ifb == NULL || surface_width(surface) != width || + surface_height(surface) != height) { DPRINT_L1("Resolution changed from %ux%u to %ux%u\n", - ds_get_width(s->console), ds_get_height(s->console), width, height); + surface_width(surface), surface_height(surface), width, height); qemu_console_resize(s->console, width, height); s->ifb = g_realloc(s->ifb, width * height * RGBA_SIZE + 1); memset(s->ifb, 0, width * height * RGBA_SIZE + 1); @@ -1241,6 +1243,7 @@ static void exynos4210_update_resolution(Exynos4210fimdState *s) static void exynos4210_fimd_update(void *opaque) { Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; + DisplaySurface *surface = qemu_console_surface(s->console); Exynos4210fimdWindow *w; int i, line; hwaddr fb_line_addr, inc_size; @@ -1253,7 +1256,7 @@ static void exynos4210_fimd_update(void *opaque) const int global_height = ((s->vidtcon[2] >> FIMD_VIDTCON2_VER_SHIFT) & FIMD_VIDTCON2_SIZE_MASK) + 1; - if (!s || !s->console || !ds_get_bits_per_pixel(s->console) || + if (!s || !s->console || !surface_bits_per_pixel(surface) || !s->enabled) { return; } @@ -1299,10 +1302,10 @@ static void exynos4210_fimd_update(void *opaque) uint8_t *d; int bpp; - bpp = ds_get_bits_per_pixel(s->console); + bpp = surface_bits_per_pixel(surface); fimd_update_putpix_qemu(bpp); bpp = (bpp + 1) >> 3; - d = ds_get_data(s->console); + d = surface_data(surface); for (line = first_line; line <= last_line; line++) { fimd_copy_line_toqemu(global_width, s->ifb + global_width * line * RGBA_SIZE, d + global_width * line * bpp); diff --git a/hw/framebuffer.c b/hw/framebuffer.c index d341aa0c6b..7326a98a41 100644 --- a/hw/framebuffer.c +++ b/hw/framebuffer.c @@ -24,7 +24,7 @@ /* Render an image from a shared memory framebuffer. */ void framebuffer_update_display( - DisplayState *ds, + DisplaySurface *ds, MemoryRegion *address_space, hwaddr base, int cols, /* Width in pixels. */ @@ -73,7 +73,7 @@ void framebuffer_update_display( return; } src = src_base; - dest = ds_get_data(ds); + dest = surface_data(ds); if (dest_col_pitch < 0) dest -= dest_col_pitch * (cols - 1); if (dest_row_pitch < 0) { diff --git a/hw/framebuffer.h b/hw/framebuffer.h index 11f53edec0..6eae035b7d 100644 --- a/hw/framebuffer.h +++ b/hw/framebuffer.h @@ -8,7 +8,7 @@ typedef void (*drawfn)(void *, uint8_t *, const uint8_t *, int, int); void framebuffer_update_display( - DisplayState *ds, + DisplaySurface *ds, MemoryRegion *address_space, hwaddr base, int cols, diff --git a/hw/g364fb.c b/hw/g364fb.c index 7b69815bc9..f7014e9dd8 100644 --- a/hw/g364fb.c +++ b/hw/g364fb.c @@ -39,7 +39,7 @@ typedef struct G364State { uint32_t top_of_screen; uint32_t width, height; /* in pixels */ /* display refresh support */ - DisplayState *ds; + QemuConsole *con; int depth; int blanked; } G364State; @@ -77,6 +77,7 @@ static inline void reset_dirty(G364State *s, static void g364fb_draw_graphic8(G364State *s) { + DisplaySurface *surface = qemu_console_surface(s->con); int i, w; uint8_t *vram; uint8_t *data_display, *dd; @@ -87,7 +88,7 @@ static void g364fb_draw_graphic8(G364State *s) int xcursor, ycursor; unsigned int (*rgb_to_pixel)(unsigned int r, unsigned int g, unsigned int b); - switch (ds_get_bits_per_pixel(s->ds)) { + switch (surface_bits_per_pixel(surface)) { case 8: rgb_to_pixel = rgb_to_pixel8; w = 1; @@ -106,7 +107,7 @@ static void g364fb_draw_graphic8(G364State *s) break; default: hw_error("g364: unknown host depth %d", - ds_get_bits_per_pixel(s->ds)); + surface_bits_per_pixel(surface)); return; } @@ -129,7 +130,7 @@ static void g364fb_draw_graphic8(G364State *s) vram = s->vram + s->top_of_screen; /* XXX: out of range in vram? */ - data_display = dd = ds_get_data(s->ds); + data_display = dd = surface_data(surface); while (y < s->height) { if (check_dirty(s, page)) { if (y < ymin) @@ -182,7 +183,7 @@ static void g364fb_draw_graphic8(G364State *s) ymax = s->height - 1; goto done; } - data_display = dd = data_display + ds_get_linesize(s->ds); + data_display = dd = data_display + surface_stride(surface); xmin = 0; x = 0; } @@ -197,7 +198,7 @@ static void g364fb_draw_graphic8(G364State *s) reset_dirty(s, page_min, page_max); page_min = (ram_addr_t)-1; page_max = 0; - dpy_gfx_update(s->ds, xmin, ymin, + dpy_gfx_update(s->con, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1); xmin = s->width; xmax = 0; @@ -209,7 +210,7 @@ static void g364fb_draw_graphic8(G364State *s) x = x % s->width; y += dy; vram += G364_PAGE_SIZE; - data_display += dy * ds_get_linesize(s->ds); + data_display += dy * surface_stride(surface); dd = data_display + x * w; } page += G364_PAGE_SIZE; @@ -217,13 +218,14 @@ static void g364fb_draw_graphic8(G364State *s) done: if (page_min != (ram_addr_t)-1) { - dpy_gfx_update(s->ds, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1); + dpy_gfx_update(s->con, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1); reset_dirty(s, page_min, page_max); } } static void g364fb_draw_blank(G364State *s) { + DisplaySurface *surface = qemu_console_surface(s->con); int i, w; uint8_t *d; @@ -232,28 +234,30 @@ static void g364fb_draw_blank(G364State *s) return; } - w = s->width * ((ds_get_bits_per_pixel(s->ds) + 7) >> 3); - d = ds_get_data(s->ds); + w = s->width * surface_bytes_per_pixel(surface); + d = surface_data(surface); for (i = 0; i < s->height; i++) { memset(d, 0, w); - d += ds_get_linesize(s->ds); + d += surface_stride(surface); } - dpy_gfx_update(s->ds, 0, 0, s->width, s->height); + dpy_gfx_update(s->con, 0, 0, s->width, s->height); s->blanked = 1; } static void g364fb_update_display(void *opaque) { G364State *s = opaque; + DisplaySurface *surface = qemu_console_surface(s->con); qemu_flush_coalesced_mmio_buffer(); if (s->width == 0 || s->height == 0) return; - if (s->width != ds_get_width(s->ds) || s->height != ds_get_height(s->ds)) { - qemu_console_resize(s->ds, s->width, s->height); + if (s->width != surface_width(surface) || + s->height != surface_height(surface)) { + qemu_console_resize(s->con, s->width, s->height); } if (s->ctla & CTLA_FORCE_BLANK) { @@ -413,13 +417,14 @@ static void g364fb_update_depth(G364State *s) static void g364_invalidate_cursor_position(G364State *s) { + DisplaySurface *surface = qemu_console_surface(s->con); int ymin, ymax, start, end; /* invalidate only near the cursor */ ymin = s->cursor_position & 0xfff; ymax = MIN(s->height, ymin + 64); - start = ymin * ds_get_linesize(s->ds); - end = (ymax + 1) * ds_get_linesize(s->ds); + start = ymin * surface_stride(surface); + end = (ymax + 1) * surface_stride(surface); memory_region_set_dirty(&s->mem_vram, start, end - start); } @@ -545,9 +550,9 @@ static void g364fb_init(DeviceState *dev, G364State *s) { s->vram = g_malloc0(s->vram_size); - s->ds = graphic_console_init(g364fb_update_display, - g364fb_invalidate_display, - g364fb_screen_dump, NULL, s); + s->con = graphic_console_init(g364fb_update_display, + g364fb_invalidate_display, + g364fb_screen_dump, NULL, s); memory_region_init_io(&s->mem_ctrl, &g364fb_ctrl_ops, s, "ctrl", 0x180000); memory_region_init_ram_ptr(&s->mem_vram, "vram", @@ -52,16 +52,22 @@ int qemu_boot_set(const char *boot_devices); #if TARGET_LONG_BITS == 64 #define VMSTATE_UINTTL_V(_f, _s, _v) \ VMSTATE_UINT64_V(_f, _s, _v) +#define VMSTATE_UINTTL_EQUAL_V(_f, _s, _v) \ + VMSTATE_UINT64_EQUAL_V(_f, _s, _v) #define VMSTATE_UINTTL_ARRAY_V(_f, _s, _n, _v) \ VMSTATE_UINT64_ARRAY_V(_f, _s, _n, _v) #else #define VMSTATE_UINTTL_V(_f, _s, _v) \ VMSTATE_UINT32_V(_f, _s, _v) +#define VMSTATE_UINTTL_EQUAL_V(_f, _s, _v) \ + VMSTATE_UINT32_EQUAL_V(_f, _s, _v) #define VMSTATE_UINTTL_ARRAY_V(_f, _s, _n, _v) \ VMSTATE_UINT32_ARRAY_V(_f, _s, _n, _v) #endif #define VMSTATE_UINTTL(_f, _s) \ VMSTATE_UINTTL_V(_f, _s, 0) +#define VMSTATE_UINTTL_EQUAL(_f, _s) \ + VMSTATE_UINTTL_EQUAL_V(_f, _s, 0) #define VMSTATE_UINTTL_ARRAY(_f, _s, _n) \ VMSTATE_UINTTL_ARRAY_V(_f, _s, _n, 0) diff --git a/hw/jazz_led.c b/hw/jazz_led.c index a418a7d1b6..05528c7c81 100644 --- a/hw/jazz_led.c +++ b/hw/jazz_led.c @@ -36,7 +36,7 @@ typedef struct LedState { SysBusDevice busdev; MemoryRegion iomem; uint8_t segments; - DisplayState *ds; + QemuConsole *con; screen_state_t state; } LedState; @@ -75,13 +75,15 @@ static const MemoryRegionOps led_ops = { /***********************************************************/ /* jazz_led display */ -static void draw_horizontal_line(DisplayState *ds, int posy, int posx1, int posx2, uint32_t color) +static void draw_horizontal_line(DisplaySurface *ds, + int posy, int posx1, int posx2, + uint32_t color) { uint8_t *d; int x, bpp; - bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3; - d = ds_get_data(ds) + ds_get_linesize(ds) * posy + bpp * posx1; + bpp = (surface_bits_per_pixel(ds) + 7) >> 3; + d = surface_data(ds) + surface_stride(ds) * posy + bpp * posx1; switch(bpp) { case 1: for (x = posx1; x <= posx2; x++) { @@ -104,30 +106,32 @@ static void draw_horizontal_line(DisplayState *ds, int posy, int posx1, int posx } } -static void draw_vertical_line(DisplayState *ds, int posx, int posy1, int posy2, uint32_t color) +static void draw_vertical_line(DisplaySurface *ds, + int posx, int posy1, int posy2, + uint32_t color) { uint8_t *d; int y, bpp; - bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3; - d = ds_get_data(ds) + ds_get_linesize(ds) * posy1 + bpp * posx; + bpp = (surface_bits_per_pixel(ds) + 7) >> 3; + d = surface_data(ds) + surface_stride(ds) * posy1 + bpp * posx; switch(bpp) { case 1: for (y = posy1; y <= posy2; y++) { *((uint8_t *)d) = color; - d += ds_get_linesize(ds); + d += surface_stride(ds); } break; case 2: for (y = posy1; y <= posy2; y++) { *((uint16_t *)d) = color; - d += ds_get_linesize(ds); + d += surface_stride(ds); } break; case 4: for (y = posy1; y <= posy2; y++) { *((uint32_t *)d) = color; - d += ds_get_linesize(ds); + d += surface_stride(ds); } break; } @@ -136,24 +140,24 @@ static void draw_vertical_line(DisplayState *ds, int posx, int posy1, int posy2, static void jazz_led_update_display(void *opaque) { LedState *s = opaque; - DisplayState *ds = s->ds; + DisplaySurface *surface = qemu_console_surface(s->con); uint8_t *d1; uint32_t color_segment, color_led; int y, bpp; if (s->state & REDRAW_BACKGROUND) { /* clear screen */ - bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3; - d1 = ds_get_data(ds); - for (y = 0; y < ds_get_height(ds); y++) { - memset(d1, 0x00, ds_get_width(ds) * bpp); - d1 += ds_get_linesize(ds); + bpp = (surface_bits_per_pixel(surface) + 7) >> 3; + d1 = surface_data(surface); + for (y = 0; y < surface_height(surface); y++) { + memset(d1, 0x00, surface_width(surface) * bpp); + d1 += surface_stride(surface); } } if (s->state & REDRAW_SEGMENTS) { /* set colors according to bpp */ - switch (ds_get_bits_per_pixel(ds)) { + switch (surface_bits_per_pixel(surface)) { case 8: color_segment = rgb_to_pixel8(0xaa, 0xaa, 0xaa); color_led = rgb_to_pixel8(0x00, 0xff, 0x00); @@ -178,26 +182,34 @@ static void jazz_led_update_display(void *opaque) } /* display segments */ - draw_horizontal_line(ds, 40, 10, 40, (s->segments & 0x02) ? color_segment : 0); - draw_vertical_line(ds, 10, 10, 40, (s->segments & 0x04) ? color_segment : 0); - draw_vertical_line(ds, 10, 40, 70, (s->segments & 0x08) ? color_segment : 0); - draw_horizontal_line(ds, 70, 10, 40, (s->segments & 0x10) ? color_segment : 0); - draw_vertical_line(ds, 40, 40, 70, (s->segments & 0x20) ? color_segment : 0); - draw_vertical_line(ds, 40, 10, 40, (s->segments & 0x40) ? color_segment : 0); - draw_horizontal_line(ds, 10, 10, 40, (s->segments & 0x80) ? color_segment : 0); + draw_horizontal_line(surface, 40, 10, 40, + (s->segments & 0x02) ? color_segment : 0); + draw_vertical_line(surface, 10, 10, 40, + (s->segments & 0x04) ? color_segment : 0); + draw_vertical_line(surface, 10, 40, 70, + (s->segments & 0x08) ? color_segment : 0); + draw_horizontal_line(surface, 70, 10, 40, + (s->segments & 0x10) ? color_segment : 0); + draw_vertical_line(surface, 40, 40, 70, + (s->segments & 0x20) ? color_segment : 0); + draw_vertical_line(surface, 40, 10, 40, + (s->segments & 0x40) ? color_segment : 0); + draw_horizontal_line(surface, 10, 10, 40, + (s->segments & 0x80) ? color_segment : 0); /* display led */ if (!(s->segments & 0x01)) color_led = 0; /* black */ - draw_horizontal_line(ds, 68, 50, 50, color_led); - draw_horizontal_line(ds, 69, 49, 51, color_led); - draw_horizontal_line(ds, 70, 48, 52, color_led); - draw_horizontal_line(ds, 71, 49, 51, color_led); - draw_horizontal_line(ds, 72, 50, 50, color_led); + draw_horizontal_line(surface, 68, 50, 50, color_led); + draw_horizontal_line(surface, 69, 49, 51, color_led); + draw_horizontal_line(surface, 70, 48, 52, color_led); + draw_horizontal_line(surface, 71, 49, 51, color_led); + draw_horizontal_line(surface, 72, 50, 50, color_led); } s->state = REDRAW_NONE; - dpy_gfx_update(ds, 0, 0, ds_get_width(ds), ds_get_height(ds)); + dpy_gfx_update(s->con, 0, 0, + surface_width(surface), surface_height(surface)); } static void jazz_led_invalidate_display(void *opaque) @@ -211,15 +223,15 @@ static void jazz_led_text_update(void *opaque, console_ch_t *chardata) LedState *s = opaque; char buf[2]; - dpy_text_cursor(s->ds, -1, -1); - qemu_console_resize(s->ds, 2, 1); + dpy_text_cursor(s->con, -1, -1); + qemu_console_resize(s->con, 2, 1); /* TODO: draw the segments */ snprintf(buf, 2, "%02hhx\n", s->segments); console_write_ch(chardata++, 0x00200100 | buf[0]); console_write_ch(chardata++, 0x00200100 | buf[1]); - dpy_text_update(s->ds, 0, 0, 2, 1); + dpy_text_update(s->con, 0, 0, 2, 1); } static int jazz_led_post_load(void *opaque, int version_id) @@ -249,10 +261,10 @@ static int jazz_led_init(SysBusDevice *dev) memory_region_init_io(&s->iomem, &led_ops, s, "led", 1); sysbus_init_mmio(dev, &s->iomem); - s->ds = graphic_console_init(jazz_led_update_display, - jazz_led_invalidate_display, - NULL, - jazz_led_text_update, s); + s->con = graphic_console_init(jazz_led_update_display, + jazz_led_invalidate_display, + NULL, + jazz_led_text_update, s); return 0; } @@ -263,7 +275,7 @@ static void jazz_led_reset(DeviceState *d) s->segments = 0; s->state = REDRAW_SEGMENTS | REDRAW_BACKGROUND; - qemu_console_resize(s->ds, 60, 80); + qemu_console_resize(s->con, 60, 80); } static void jazz_led_class_init(ObjectClass *klass, void *data) diff --git a/hw/lm32/Makefile.objs b/hw/lm32/Makefile.objs index 4592fe5fc8..68ca90a3e2 100644 --- a/hw/lm32/Makefile.objs +++ b/hw/lm32/Makefile.objs @@ -11,7 +11,7 @@ obj-y += milkymist-minimac2.o obj-y += milkymist-pfpu.o obj-y += milkymist-softusb.o obj-y += milkymist-sysctl.o -obj-$(CONFIG_OPENGL) += milkymist-tmu2.o +obj-$(CONFIG_GLX) += milkymist-tmu2.o obj-y += milkymist-uart.o obj-y += milkymist-vgafb.o obj-y += framebuffer.o diff --git a/hw/lm32_uart.c b/hw/lm32_uart.c index 02f6f89174..32bc37ac96 100644 --- a/hw/lm32_uart.c +++ b/hw/lm32_uart.c @@ -137,6 +137,7 @@ static uint64_t uart_read(void *opaque, hwaddr addr, r = s->regs[R_RXTX]; s->regs[R_LSR] &= ~LSR_DR; uart_update_irq(s); + qemu_chr_accept_input(s->chr); break; case R_IIR: case R_LSR: diff --git a/hw/milkymist-hw.h b/hw/milkymist-hw.h index ced1c5f54e..c047a70a9f 100644 --- a/hw/milkymist-hw.h +++ b/hw/milkymist-hw.h @@ -87,7 +87,7 @@ static inline DeviceState *milkymist_pfpu_create(hwaddr base, return dev; } -#ifdef CONFIG_OPENGL +#ifdef CONFIG_GLX #include <X11/Xlib.h> #include <GL/glx.h> static const int glx_fbconfig_attr[] = { @@ -101,7 +101,7 @@ static const int glx_fbconfig_attr[] = { static inline DeviceState *milkymist_tmu2_create(hwaddr base, qemu_irq irq) { -#ifdef CONFIG_OPENGL +#ifdef CONFIG_GLX DeviceState *dev; Display *d; GLXFBConfig *configs; diff --git a/hw/milkymist-uart.c b/hw/milkymist-uart.c index ac6f5373ad..f3bdf6991a 100644 --- a/hw/milkymist-uart.c +++ b/hw/milkymist-uart.c @@ -132,6 +132,7 @@ static void uart_write(void *opaque, hwaddr addr, uint64_t value, case R_STAT: /* write one to clear bits */ s->regs[addr] &= ~(value & (STAT_RX_EVT | STAT_TX_EVT)); + qemu_chr_accept_input(s->chr); break; default: diff --git a/hw/milkymist-vgafb.c b/hw/milkymist-vgafb.c index 85ebb851bd..98762ecd21 100644 --- a/hw/milkymist-vgafb.c +++ b/hw/milkymist-vgafb.c @@ -66,7 +66,7 @@ enum { struct MilkymistVgafbState { SysBusDevice busdev; MemoryRegion regs_region; - DisplayState *ds; + QemuConsole *con; int invalidate; uint32_t fb_offset; @@ -84,6 +84,7 @@ static int vgafb_enabled(MilkymistVgafbState *s) static void vgafb_update_display(void *opaque) { MilkymistVgafbState *s = opaque; + DisplaySurface *surface = qemu_console_surface(s->con); int first = 0; int last = 0; drawfn fn; @@ -94,7 +95,7 @@ static void vgafb_update_display(void *opaque) int dest_width = s->regs[R_HRES]; - switch (ds_get_bits_per_pixel(s->ds)) { + switch (surface_bits_per_pixel(surface)) { case 0: return; case 8: @@ -121,7 +122,7 @@ static void vgafb_update_display(void *opaque) break; } - framebuffer_update_display(s->ds, sysbus_address_space(&s->busdev), + framebuffer_update_display(surface, sysbus_address_space(&s->busdev), s->regs[R_BASEADDRESS] + s->fb_offset, s->regs[R_HRES], s->regs[R_VRES], @@ -134,7 +135,7 @@ static void vgafb_update_display(void *opaque) &first, &last); if (first >= 0) { - dpy_gfx_update(s->ds, 0, first, s->regs[R_HRES], last - first + 1); + dpy_gfx_update(s->con, 0, first, s->regs[R_HRES], last - first + 1); } s->invalidate = 0; } @@ -151,7 +152,7 @@ static void vgafb_resize(MilkymistVgafbState *s) return; } - qemu_console_resize(s->ds, s->regs[R_HRES], s->regs[R_VRES]); + qemu_console_resize(s->con, s->regs[R_HRES], s->regs[R_VRES]); s->invalidate = 1; } @@ -277,9 +278,9 @@ static int milkymist_vgafb_init(SysBusDevice *dev) "milkymist-vgafb", R_MAX * 4); sysbus_init_mmio(dev, &s->regs_region); - s->ds = graphic_console_init(vgafb_update_display, - vgafb_invalidate_display, - NULL, NULL, s); + s->con = graphic_console_init(vgafb_update_display, + vgafb_invalidate_display, + NULL, NULL, s); return 0; } diff --git a/hw/moxie/Makefile.objs b/hw/moxie/Makefile.objs new file mode 100644 index 0000000000..a5f1742936 --- /dev/null +++ b/hw/moxie/Makefile.objs @@ -0,0 +1,5 @@ +# moxie boards +obj-y = serial.o mc146818rtc.o vga.o + +obj-y := $(addprefix ../,$(obj-y)) +obj-y += moxiesim.o diff --git a/hw/moxie/moxiesim.c b/hw/moxie/moxiesim.c new file mode 100644 index 0000000000..e1e88a9c30 --- /dev/null +++ b/hw/moxie/moxiesim.c @@ -0,0 +1,174 @@ +/* + * QEMU/moxiesim emulation + * + * Emulates a very simple machine model similiar to the one use by the + * GDB moxie simulator. + * + * Copyright (c) 2008, 2009, 2010, 2013 Anthony Green + * + * 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 "hw/sysbus.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/isa.h" +#include "net/net.h" +#include "sysemu/sysemu.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "hw/serial.h" +#include "exec/address-spaces.h" + +#define PHYS_MEM_BASE 0x80000000 + +typedef struct { + uint64_t ram_size; + const char *kernel_filename; + const char *kernel_cmdline; + const char *initrd_filename; +} LoaderParams; + +static void load_kernel(MoxieCPU *cpu, LoaderParams *loader_params) +{ + uint64_t entry, kernel_low, kernel_high; + long kernel_size; + long initrd_size; + ram_addr_t initrd_offset; + + kernel_size = load_elf(loader_params->kernel_filename, NULL, NULL, + &entry, &kernel_low, &kernel_high, 1, + ELF_MACHINE, 0); + + if (!kernel_size) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + loader_params->kernel_filename); + exit(1); + } + + /* load initrd */ + initrd_size = 0; + initrd_offset = 0; + if (loader_params->initrd_filename) { + initrd_size = get_image_size(loader_params->initrd_filename); + if (initrd_size > 0) { + initrd_offset = (kernel_high + ~TARGET_PAGE_MASK) + & TARGET_PAGE_MASK; + if (initrd_offset + initrd_size > loader_params->ram_size) { + fprintf(stderr, + "qemu: memory too small for initial ram disk '%s'\n", + loader_params->initrd_filename); + exit(1); + } + initrd_size = load_image_targphys(loader_params->initrd_filename, + initrd_offset, + ram_size); + } + if (initrd_size == (target_ulong)-1) { + fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", + loader_params->initrd_filename); + exit(1); + } + } +} + +static void main_cpu_reset(void *opaque) +{ + MoxieCPU *cpu = opaque; + + cpu_reset(CPU(cpu)); +} + +static inline DeviceState * +moxie_intc_create(hwaddr base, qemu_irq irq, int kind_of_intr) +{ + DeviceState *dev; + + dev = qdev_create(NULL, "moxie,intc"); + qdev_prop_set_uint32(dev, "kind-of-intr", kind_of_intr); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); + return dev; +} + +static void moxiesim_init(QEMUMachineInitArgs *args) +{ + MoxieCPU *cpu = NULL; + ram_addr_t ram_size = args->ram_size; + const char *cpu_model = args->cpu_model; + const char *kernel_filename = args->kernel_filename; + const char *kernel_cmdline = args->kernel_cmdline; + const char *initrd_filename = args->initrd_filename; + CPUMoxieState *env; + MemoryRegion *address_space_mem = get_system_memory(); + MemoryRegion *ram = g_new(MemoryRegion, 1); + MemoryRegion *rom = g_new(MemoryRegion, 1); + hwaddr ram_base = 0x200000; + LoaderParams loader_params; + + /* Init CPUs. */ + if (cpu_model == NULL) { + cpu_model = "MoxieLite-moxie-cpu"; + } + cpu = cpu_moxie_init(cpu_model); + if (!cpu) { + fprintf(stderr, "Unable to find CPU definition\n"); + exit(1); + } + env = &cpu->env; + + qemu_register_reset(main_cpu_reset, cpu); + + /* Allocate RAM. */ + memory_region_init_ram(ram, "moxiesim.ram", ram_size); + vmstate_register_ram_global(ram); + memory_region_add_subregion(address_space_mem, ram_base, ram); + + memory_region_init_ram(rom, "moxie.rom", 128*0x1000); + vmstate_register_ram_global(rom); + memory_region_add_subregion(get_system_memory(), 0x1000, rom); + + if (kernel_filename) { + loader_params.ram_size = ram_size; + loader_params.kernel_filename = kernel_filename; + loader_params.kernel_cmdline = kernel_cmdline; + loader_params.initrd_filename = initrd_filename; + load_kernel(cpu, &loader_params); + } + + /* A single 16450 sits at offset 0x3f8. */ + if (serial_hds[0]) { + serial_mm_init(address_space_mem, 0x3f8, 0, env->irq[4], + 8000000/16, serial_hds[0], DEVICE_LITTLE_ENDIAN); + } +} + +static QEMUMachine moxiesim_machine = { + .name = "moxiesim", + .desc = "Moxie simulator platform", + .init = moxiesim_init, + .is_default = 1, +}; + +static void moxie_machine_init(void) +{ + qemu_register_machine(&moxiesim_machine); +} + +machine_init(moxie_machine_init) diff --git a/hw/ne2000.c b/hw/ne2000.c index 7dadc1cea7..7f458311c6 100644 --- a/hw/ne2000.c +++ b/hw/ne2000.c @@ -766,7 +766,7 @@ static void ne2000_class_init(ObjectClass *klass, void *data) k->init = pci_ne2000_init; k->exit = pci_ne2000_exit; - k->romfile = "pxe-ne2k_pci.rom", + k->romfile = "efi-ne2k_pci.rom", k->vendor_id = PCI_VENDOR_ID_REALTEK; k->device_id = PCI_DEVICE_ID_REALTEK_8029; k->class_id = PCI_CLASS_NETWORK_ETHERNET; diff --git a/hw/omap_lcdc.c b/hw/omap_lcdc.c index c426f3a13a..4f5b0947cf 100644 --- a/hw/omap_lcdc.c +++ b/hw/omap_lcdc.c @@ -26,7 +26,7 @@ struct omap_lcd_panel_s { MemoryRegion *sysmem; MemoryRegion iomem; qemu_irq irq; - DisplayState *state; + QemuConsole *con; int plm; int tft; @@ -113,14 +113,16 @@ static draw_line_func draw_line_table2[33] = { static void omap_update_display(void *opaque) { struct omap_lcd_panel_s *omap_lcd = (struct omap_lcd_panel_s *) opaque; + DisplaySurface *surface = qemu_console_surface(omap_lcd->con); draw_line_func draw_line; int size, height, first, last; int width, linesize, step, bpp, frame_offset; hwaddr frame_base; - if (!omap_lcd || omap_lcd->plm == 1 || - !omap_lcd->enable || !ds_get_bits_per_pixel(omap_lcd->state)) + if (!omap_lcd || omap_lcd->plm == 1 || !omap_lcd->enable || + !surface_bits_per_pixel(surface)) { return; + } frame_offset = 0; if (omap_lcd->plm != 2) { @@ -139,25 +141,25 @@ static void omap_update_display(void *opaque) /* Colour depth */ switch ((omap_lcd->palette[0] >> 12) & 7) { case 1: - draw_line = draw_line_table2[ds_get_bits_per_pixel(omap_lcd->state)]; + draw_line = draw_line_table2[surface_bits_per_pixel(surface)]; bpp = 2; break; case 2: - draw_line = draw_line_table4[ds_get_bits_per_pixel(omap_lcd->state)]; + draw_line = draw_line_table4[surface_bits_per_pixel(surface)]; bpp = 4; break; case 3: - draw_line = draw_line_table8[ds_get_bits_per_pixel(omap_lcd->state)]; + draw_line = draw_line_table8[surface_bits_per_pixel(surface)]; bpp = 8; break; case 4 ... 7: if (!omap_lcd->tft) - draw_line = draw_line_table12[ds_get_bits_per_pixel(omap_lcd->state)]; + draw_line = draw_line_table12[surface_bits_per_pixel(surface)]; else - draw_line = draw_line_table16[ds_get_bits_per_pixel(omap_lcd->state)]; + draw_line = draw_line_table16[surface_bits_per_pixel(surface)]; bpp = 16; break; @@ -168,10 +170,11 @@ static void omap_update_display(void *opaque) /* Resolution */ width = omap_lcd->width; - if (width != ds_get_width(omap_lcd->state) || - omap_lcd->height != ds_get_height(omap_lcd->state)) { - qemu_console_resize(omap_lcd->state, + if (width != surface_width(surface) || + omap_lcd->height != surface_height(surface)) { + qemu_console_resize(omap_lcd->con, omap_lcd->width, omap_lcd->height); + surface = qemu_console_surface(omap_lcd->con); omap_lcd->invalidate = 1; } @@ -196,8 +199,9 @@ static void omap_update_display(void *opaque) if (omap_lcd->dma->dual) omap_lcd->dma->current_frame ^= 1; - if (!ds_get_bits_per_pixel(omap_lcd->state)) + if (!surface_bits_per_pixel(surface)) { return; + } first = 0; height = omap_lcd->height; @@ -210,15 +214,15 @@ static void omap_update_display(void *opaque) } step = width * bpp >> 3; - linesize = ds_get_linesize(omap_lcd->state); - framebuffer_update_display(omap_lcd->state, omap_lcd->sysmem, + linesize = surface_stride(surface); + framebuffer_update_display(surface, omap_lcd->sysmem, frame_base, width, height, step, linesize, 0, omap_lcd->invalidate, draw_line, omap_lcd->palette, &first, &last); if (first >= 0) { - dpy_gfx_update(omap_lcd->state, 0, first, width, last - first + 1); + dpy_gfx_update(omap_lcd->con, 0, first, width, last - first + 1); } omap_lcd->invalidate = 0; } @@ -298,12 +302,13 @@ static void omap_screen_dump(void *opaque, const char *filename, bool cswitch, Error **errp) { struct omap_lcd_panel_s *omap_lcd = opaque; + DisplaySurface *surface = qemu_console_surface(omap_lcd->con); omap_update_display(opaque); - if (omap_lcd && ds_get_data(omap_lcd->state)) - omap_ppm_save(filename, ds_get_data(omap_lcd->state), + if (omap_lcd && surface_data(surface)) + omap_ppm_save(filename, surface_data(surface), omap_lcd->width, omap_lcd->height, - ds_get_linesize(omap_lcd->state), errp); + surface_stride(surface), errp); } static void omap_invalidate_display(void *opaque) { @@ -480,9 +485,9 @@ struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem, memory_region_init_io(&s->iomem, &omap_lcdc_ops, s, "omap.lcdc", 0x100); memory_region_add_subregion(sysmem, base, &s->iomem); - s->state = graphic_console_init(omap_update_display, - omap_invalidate_display, - omap_screen_dump, NULL, s); + s->con = graphic_console_init(omap_update_display, + omap_invalidate_display, + omap_screen_dump, NULL, s); return s; } @@ -212,7 +212,7 @@ int e820_add_entry(uint64_t, uint64_t, uint32_t); .driver = "ide-drive",\ .property = "discard_granularity",\ .value = stringify(0),\ - },{\ + },{\ .driver = "virtio-blk-pci",\ .property = "discard_granularity",\ .value = stringify(0),\ @@ -221,6 +221,26 @@ int e820_add_entry(uint64_t, uint64_t, uint32_t); .property = "vectors",\ /* DEV_NVECTORS_UNSPECIFIED as a uint32_t string */\ .value = stringify(0xFFFFFFFF),\ - } + },{\ + .driver = "e1000",\ + .property = "romfile",\ + .value = "pxe-e1000.rom",\ + },{\ + .driver = "ne2k_pci",\ + .property = "romfile",\ + .value = "pxe-ne2k_pci.rom",\ + },{\ + .driver = "pcnet",\ + .property = "romfile",\ + .value = "pxe-pcnet.rom",\ + },{\ + .driver = "rtl8139",\ + .property = "romfile",\ + .value = "pxe-rtl8139.rom",\ + },{\ + .driver = "virtio-net-pci",\ + .property = "romfile",\ + .value = "pxe-virtio.rom",\ + } #endif diff --git a/hw/pci/pci.h b/hw/pci/pci.h index 774369cd9e..9ea67a3832 100644 --- a/hw/pci/pci.h +++ b/hw/pci/pci.h @@ -60,6 +60,7 @@ #define PCI_DEVICE_ID_VMWARE_NET 0x0720 #define PCI_DEVICE_ID_VMWARE_SCSI 0x0730 #define PCI_DEVICE_ID_VMWARE_IDE 0x1729 +#define PCI_DEVICE_ID_VMWARE_VMXNET3 0x07B0 /* Intel (0x8086) */ #define PCI_DEVICE_ID_INTEL_82551IT 0x1209 diff --git a/hw/pci/pci_host.c b/hw/pci/pci_host.c index daca1c1ea0..12254b18a9 100644 --- a/hw/pci/pci_host.c +++ b/hw/pci/pci_host.c @@ -38,7 +38,7 @@ do { printf("pci_host_data: " fmt , ## __VA_ARGS__); } while (0) * bit 0 - 7: offset in configuration space of a given pci device */ -/* the helper functio to get a PCIDeice* for a given pci address */ +/* the helper function to get a PCIDevice* for a given pci address */ static inline PCIDevice *pci_dev_find_by_addr(PCIBus *bus, uint32_t addr) { uint8_t bus_num = addr >> 16; diff --git a/hw/pcnet-pci.c b/hw/pcnet-pci.c index 55f80ca671..61af57ed51 100644 --- a/hw/pcnet-pci.c +++ b/hw/pcnet-pci.c @@ -351,7 +351,7 @@ static void pcnet_class_init(ObjectClass *klass, void *data) k->init = pci_pcnet_init; k->exit = pci_pcnet_uninit; - k->romfile = "pxe-pcnet.rom", + k->romfile = "efi-pcnet.rom", k->vendor_id = PCI_VENDOR_ID_AMD; k->device_id = PCI_DEVICE_ID_AMD_LANCE; k->revision = 0x10; diff --git a/hw/pl110.c b/hw/pl110.c index 924642d697..fbef675f9c 100644 --- a/hw/pl110.c +++ b/hw/pl110.c @@ -42,7 +42,7 @@ enum pl110_version typedef struct { SysBusDevice busdev; MemoryRegion iomem; - DisplayState *ds; + QemuConsole *con; int version; uint32_t timing[4]; @@ -129,6 +129,7 @@ static int pl110_enabled(pl110_state *s) static void pl110_update_display(void *opaque) { pl110_state *s = (pl110_state *)opaque; + DisplaySurface *surface = qemu_console_surface(s->con); drawfn* fntable; drawfn fn; int dest_width; @@ -140,7 +141,7 @@ static void pl110_update_display(void *opaque) if (!pl110_enabled(s)) return; - switch (ds_get_bits_per_pixel(s->ds)) { + switch (surface_bits_per_pixel(surface)) { case 0: return; case 8: @@ -231,14 +232,14 @@ static void pl110_update_display(void *opaque) } dest_width *= s->cols; first = 0; - framebuffer_update_display(s->ds, sysbus_address_space(&s->busdev), + framebuffer_update_display(surface, sysbus_address_space(&s->busdev), s->upbase, s->cols, s->rows, src_width, dest_width, 0, s->invalidate, fn, s->palette, &first, &last); if (first >= 0) { - dpy_gfx_update(s->ds, 0, first, s->cols, last - first + 1); + dpy_gfx_update(s->con, 0, first, s->cols, last - first + 1); } s->invalidate = 0; } @@ -248,12 +249,13 @@ static void pl110_invalidate_display(void * opaque) pl110_state *s = (pl110_state *)opaque; s->invalidate = 1; if (pl110_enabled(s)) { - qemu_console_resize(s->ds, s->cols, s->rows); + qemu_console_resize(s->con, s->cols, s->rows); } } static void pl110_update_palette(pl110_state *s, int n) { + DisplaySurface *surface = qemu_console_surface(s->con); int i; uint32_t raw; unsigned int r, g, b; @@ -268,7 +270,7 @@ static void pl110_update_palette(pl110_state *s, int n) b = (raw & 0x1f) << 3; /* The I bit is ignored. */ raw >>= 6; - switch (ds_get_bits_per_pixel(s->ds)) { + switch (surface_bits_per_pixel(surface)) { case 8: s->palette[n] = rgb_to_pixel8(r, g, b); break; @@ -291,7 +293,7 @@ static void pl110_resize(pl110_state *s, int width, int height) { if (width != s->cols || height != s->rows) { if (pl110_enabled(s)) { - qemu_console_resize(s->ds, width, height); + qemu_console_resize(s->con, width, height); } } s->cols = width; @@ -409,7 +411,7 @@ static void pl110_write(void *opaque, hwaddr offset, s->cr = val; s->bpp = (val >> 1) & 7; if (pl110_enabled(s)) { - qemu_console_resize(s->ds, s->cols, s->rows); + qemu_console_resize(s->con, s->cols, s->rows); } break; case 10: /* LCDICR */ @@ -450,9 +452,9 @@ static int pl110_init(SysBusDevice *dev) sysbus_init_mmio(dev, &s->iomem); sysbus_init_irq(dev, &s->irq); qdev_init_gpio_in(&s->busdev.qdev, pl110_mux_ctrl_set, 1); - s->ds = graphic_console_init(pl110_update_display, - pl110_invalidate_display, - NULL, NULL, s); + s->con = graphic_console_init(pl110_update_display, + pl110_invalidate_display, + NULL, NULL, s); return 0; } diff --git a/hw/pl330.c b/hw/pl330.c new file mode 100644 index 0000000000..1a04773a71 --- /dev/null +++ b/hw/pl330.c @@ -0,0 +1,1654 @@ +/* + * ARM PrimeCell PL330 DMA Controller + * + * Copyright (c) 2009 Samsung Electronics. + * Contributed by Kirill Batuzov <batuzovk@ispras.ru> + * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com) + * Copyright (c) 2012 PetaLogix Pty Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 or later. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "sysbus.h" +#include "qemu/timer.h" +#include "sysemu/dma.h" + +#ifndef PL330_ERR_DEBUG +#define PL330_ERR_DEBUG 0 +#endif + +#define DB_PRINT_L(lvl, fmt, args...) do {\ + if (PL330_ERR_DEBUG >= lvl) {\ + fprintf(stderr, "PL330: %s:" fmt, __func__, ## args);\ + } \ +} while (0); + +#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args) + +#define PL330_PERIPH_NUM 32 +#define PL330_MAX_BURST_LEN 128 +#define PL330_INSN_MAXSIZE 6 + +#define PL330_FIFO_OK 0 +#define PL330_FIFO_STALL 1 +#define PL330_FIFO_ERR (-1) + +#define PL330_FAULT_UNDEF_INSTR (1 << 0) +#define PL330_FAULT_OPERAND_INVALID (1 << 1) +#define PL330_FAULT_DMAGO_ERR (1 << 4) +#define PL330_FAULT_EVENT_ERR (1 << 5) +#define PL330_FAULT_CH_PERIPH_ERR (1 << 6) +#define PL330_FAULT_CH_RDWR_ERR (1 << 7) +#define PL330_FAULT_ST_DATA_UNAVAILABLE (1 << 12) +#define PL330_FAULT_FIFOEMPTY_ERR (1 << 13) +#define PL330_FAULT_INSTR_FETCH_ERR (1 << 16) +#define PL330_FAULT_DATA_WRITE_ERR (1 << 17) +#define PL330_FAULT_DATA_READ_ERR (1 << 18) +#define PL330_FAULT_DBG_INSTR (1 << 30) +#define PL330_FAULT_LOCKUP_ERR (1 << 31) + +#define PL330_UNTAGGED 0xff + +#define PL330_SINGLE 0x0 +#define PL330_BURST 0x1 + +#define PL330_WATCHDOG_LIMIT 1024 + +/* IOMEM mapped registers */ +#define PL330_REG_DSR 0x000 +#define PL330_REG_DPC 0x004 +#define PL330_REG_INTEN 0x020 +#define PL330_REG_INT_EVENT_RIS 0x024 +#define PL330_REG_INTMIS 0x028 +#define PL330_REG_INTCLR 0x02C +#define PL330_REG_FSRD 0x030 +#define PL330_REG_FSRC 0x034 +#define PL330_REG_FTRD 0x038 +#define PL330_REG_FTR_BASE 0x040 +#define PL330_REG_CSR_BASE 0x100 +#define PL330_REG_CPC_BASE 0x104 +#define PL330_REG_CHANCTRL 0x400 +#define PL330_REG_DBGSTATUS 0xD00 +#define PL330_REG_DBGCMD 0xD04 +#define PL330_REG_DBGINST0 0xD08 +#define PL330_REG_DBGINST1 0xD0C +#define PL330_REG_CR0_BASE 0xE00 +#define PL330_REG_PERIPH_ID 0xFE0 + +#define PL330_IOMEM_SIZE 0x1000 + +#define CFG_BOOT_ADDR 2 +#define CFG_INS 3 +#define CFG_PNS 4 +#define CFG_CRD 5 + +static const uint32_t pl330_id[] = { + 0x30, 0x13, 0x24, 0x00, 0x0D, 0xF0, 0x05, 0xB1 +}; + +/* DMA channel states as they are described in PL330 Technical Reference Manual + * Most of them will not be used in emulation. + */ +typedef enum { + pl330_chan_stopped = 0, + pl330_chan_executing = 1, + pl330_chan_cache_miss = 2, + pl330_chan_updating_pc = 3, + pl330_chan_waiting_event = 4, + pl330_chan_at_barrier = 5, + pl330_chan_queue_busy = 6, + pl330_chan_waiting_periph = 7, + pl330_chan_killing = 8, + pl330_chan_completing = 9, + pl330_chan_fault_completing = 14, + pl330_chan_fault = 15, +} PL330ChanState; + +typedef struct PL330State PL330State; + +typedef struct PL330Chan { + uint32_t src; + uint32_t dst; + uint32_t pc; + uint32_t control; + uint32_t status; + uint32_t lc[2]; + uint32_t fault_type; + uint32_t watchdog_timer; + + bool ns; + uint8_t request_flag; + uint8_t wakeup; + uint8_t wfp_sbp; + + uint8_t state; + uint8_t stall; + + bool is_manager; + PL330State *parent; + uint8_t tag; +} PL330Chan; + +static const VMStateDescription vmstate_pl330_chan = { + .name = "pl330_chan", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(src, PL330Chan), + VMSTATE_UINT32(dst, PL330Chan), + VMSTATE_UINT32(pc, PL330Chan), + VMSTATE_UINT32(control, PL330Chan), + VMSTATE_UINT32(status, PL330Chan), + VMSTATE_UINT32_ARRAY(lc, PL330Chan, 2), + VMSTATE_UINT32(fault_type, PL330Chan), + VMSTATE_UINT32(watchdog_timer, PL330Chan), + VMSTATE_BOOL(ns, PL330Chan), + VMSTATE_UINT8(request_flag, PL330Chan), + VMSTATE_UINT8(wakeup, PL330Chan), + VMSTATE_UINT8(wfp_sbp, PL330Chan), + VMSTATE_UINT8(state, PL330Chan), + VMSTATE_UINT8(stall, PL330Chan), + VMSTATE_END_OF_LIST() + } +}; + +typedef struct PL330Fifo { + uint8_t *buf; + uint8_t *tag; + uint32_t head; + uint32_t num; + uint32_t buf_size; +} PL330Fifo; + +static const VMStateDescription vmstate_pl330_fifo = { + .name = "pl330_chan", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_VBUFFER_UINT32(buf, PL330Fifo, 1, NULL, 0, buf_size), + VMSTATE_VBUFFER_UINT32(tag, PL330Fifo, 1, NULL, 0, buf_size), + VMSTATE_UINT32(head, PL330Fifo), + VMSTATE_UINT32(num, PL330Fifo), + VMSTATE_UINT32(buf_size, PL330Fifo), + VMSTATE_END_OF_LIST() + } +}; + +typedef struct PL330QueueEntry { + uint32_t addr; + uint32_t len; + uint8_t n; + bool inc; + bool z; + uint8_t tag; + uint8_t seqn; +} PL330QueueEntry; + +static const VMStateDescription vmstate_pl330_queue_entry = { + .name = "pl330_queue_entry", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(addr, PL330QueueEntry), + VMSTATE_UINT32(len, PL330QueueEntry), + VMSTATE_UINT8(n, PL330QueueEntry), + VMSTATE_BOOL(inc, PL330QueueEntry), + VMSTATE_BOOL(z, PL330QueueEntry), + VMSTATE_UINT8(tag, PL330QueueEntry), + VMSTATE_UINT8(seqn, PL330QueueEntry), + VMSTATE_END_OF_LIST() + } +}; + +typedef struct PL330Queue { + PL330State *parent; + PL330QueueEntry *queue; + uint32_t queue_size; +} PL330Queue; + +static const VMStateDescription vmstate_pl330_queue = { + .name = "pl330_queue", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_VARRAY_UINT32(queue, PL330Queue, queue_size, 1, + vmstate_pl330_queue_entry, PL330QueueEntry), + VMSTATE_END_OF_LIST() + } +}; + +struct PL330State { + SysBusDevice busdev; + MemoryRegion iomem; + qemu_irq irq_abort; + qemu_irq *irq; + + /* Config registers. cfg[5] = CfgDn. */ + uint32_t cfg[6]; +#define EVENT_SEC_STATE 3 +#define PERIPH_SEC_STATE 4 + /* cfg 0 bits and pieces */ + uint32_t num_chnls; + uint8_t num_periph_req; + uint8_t num_events; + uint8_t mgr_ns_at_rst; + /* cfg 1 bits and pieces */ + uint8_t i_cache_len; + uint8_t num_i_cache_lines; + /* CRD bits and pieces */ + uint8_t data_width; + uint8_t wr_cap; + uint8_t wr_q_dep; + uint8_t rd_cap; + uint8_t rd_q_dep; + uint16_t data_buffer_dep; + + PL330Chan manager; + PL330Chan *chan; + PL330Fifo fifo; + PL330Queue read_queue; + PL330Queue write_queue; + uint8_t *lo_seqn; + uint8_t *hi_seqn; + QEMUTimer *timer; /* is used for restore dma. */ + + uint32_t inten; + uint32_t int_status; + uint32_t ev_status; + uint32_t dbg[2]; + uint8_t debug_status; + uint8_t num_faulting; + uint8_t periph_busy[PL330_PERIPH_NUM]; + +}; + +#define TYPE_PL330 "pl330" +#define PL330(obj) OBJECT_CHECK(PL330State, (obj), TYPE_PL330) + +static const VMStateDescription vmstate_pl330 = { + .name = "pl330", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(manager, PL330State, 0, vmstate_pl330_chan, PL330Chan), + VMSTATE_STRUCT_VARRAY_UINT32(chan, PL330State, num_chnls, 0, + vmstate_pl330_chan, PL330Chan), + VMSTATE_VBUFFER_UINT32(lo_seqn, PL330State, 1, NULL, 0, num_chnls), + VMSTATE_VBUFFER_UINT32(hi_seqn, PL330State, 1, NULL, 0, num_chnls), + VMSTATE_STRUCT(fifo, PL330State, 0, vmstate_pl330_fifo, PL330Fifo), + VMSTATE_STRUCT(read_queue, PL330State, 0, vmstate_pl330_queue, + PL330Queue), + VMSTATE_STRUCT(write_queue, PL330State, 0, vmstate_pl330_queue, + PL330Queue), + VMSTATE_TIMER(timer, PL330State), + VMSTATE_UINT32(inten, PL330State), + VMSTATE_UINT32(int_status, PL330State), + VMSTATE_UINT32(ev_status, PL330State), + VMSTATE_UINT32_ARRAY(dbg, PL330State, 2), + VMSTATE_UINT8(debug_status, PL330State), + VMSTATE_UINT8(num_faulting, PL330State), + VMSTATE_UINT8_ARRAY(periph_busy, PL330State, PL330_PERIPH_NUM), + VMSTATE_END_OF_LIST() + } +}; + +typedef struct PL330InsnDesc { + /* OPCODE of the instruction */ + uint8_t opcode; + /* Mask so we can select several sibling instructions, such as + DMALD, DMALDS and DMALDB */ + uint8_t opmask; + /* Size of instruction in bytes */ + uint8_t size; + /* Interpreter */ + void (*exec)(PL330Chan *, uint8_t opcode, uint8_t *args, int len); +} PL330InsnDesc; + + +/* MFIFO Implementation + * + * MFIFO is implemented as a cyclic buffer of BUF_SIZE size. Tagged bytes are + * stored in this buffer. Data is stored in BUF field, tags - in the + * corresponding array elements of TAG field. + */ + +/* Initialize queue. */ + +static void pl330_fifo_init(PL330Fifo *s, uint32_t size) +{ + s->buf = g_malloc0(size); + s->tag = g_malloc0(size); + s->buf_size = size; +} + +/* Cyclic increment */ + +static inline int pl330_fifo_inc(PL330Fifo *s, int x) +{ + return (x + 1) % s->buf_size; +} + +/* Number of empty bytes in MFIFO */ + +static inline int pl330_fifo_num_free(PL330Fifo *s) +{ + return s->buf_size - s->num; +} + +/* Push LEN bytes of data stored in BUF to MFIFO and tag it with TAG. + * Zero returned on success, PL330_FIFO_STALL if there is no enough free + * space in MFIFO to store requested amount of data. If push was unsuccessful + * no data is stored to MFIFO. + */ + +static int pl330_fifo_push(PL330Fifo *s, uint8_t *buf, int len, uint8_t tag) +{ + int i; + + if (s->buf_size - s->num < len) { + return PL330_FIFO_STALL; + } + for (i = 0; i < len; i++) { + int push_idx = (s->head + s->num + i) % s->buf_size; + s->buf[push_idx] = buf[i]; + s->tag[push_idx] = tag; + } + s->num += len; + return PL330_FIFO_OK; +} + +/* Get LEN bytes of data from MFIFO and store it to BUF. Tag value of each + * byte is verified. Zero returned on success, PL330_FIFO_ERR on tag mismatch + * and PL330_FIFO_STALL if there is no enough data in MFIFO. If get was + * unsuccessful no data is removed from MFIFO. + */ + +static int pl330_fifo_get(PL330Fifo *s, uint8_t *buf, int len, uint8_t tag) +{ + int i; + + if (s->num < len) { + return PL330_FIFO_STALL; + } + for (i = 0; i < len; i++) { + if (s->tag[s->head] == tag) { + int get_idx = (s->head + i) % s->buf_size; + buf[i] = s->buf[get_idx]; + } else { /* Tag mismatch - Rollback transaction */ + return PL330_FIFO_ERR; + } + } + s->head = (s->head + len) % s->buf_size; + s->num -= len; + return PL330_FIFO_OK; +} + +/* Reset MFIFO. This completely erases all data in it. */ + +static inline void pl330_fifo_reset(PL330Fifo *s) +{ + s->head = 0; + s->num = 0; +} + +/* Return tag of the first byte stored in MFIFO. If MFIFO is empty + * PL330_UNTAGGED is returned. + */ + +static inline uint8_t pl330_fifo_tag(PL330Fifo *s) +{ + return (!s->num) ? PL330_UNTAGGED : s->tag[s->head]; +} + +/* Returns non-zero if tag TAG is present in fifo or zero otherwise */ + +static int pl330_fifo_has_tag(PL330Fifo *s, uint8_t tag) +{ + int i, n; + + i = s->head; + for (n = 0; n < s->num; n++) { + if (s->tag[i] == tag) { + return 1; + } + i = pl330_fifo_inc(s, i); + } + return 0; +} + +/* Remove all entry tagged with TAG from MFIFO */ + +static void pl330_fifo_tagged_remove(PL330Fifo *s, uint8_t tag) +{ + int i, t, n; + + t = i = s->head; + for (n = 0; n < s->num; n++) { + if (s->tag[i] != tag) { + s->buf[t] = s->buf[i]; + s->tag[t] = s->tag[i]; + t = pl330_fifo_inc(s, t); + } else { + s->num = s->num - 1; + } + i = pl330_fifo_inc(s, i); + } +} + +/* Read-Write Queue implementation + * + * A Read-Write Queue stores up to QUEUE_SIZE instructions (loads or stores). + * Each instruction is described by source (for loads) or destination (for + * stores) address ADDR, width of data to be loaded/stored LEN, number of + * stores/loads to be performed N, INC bit, Z bit and TAG to identify channel + * this instruction belongs to. Queue does not store any information about + * nature of the instruction: is it load or store. PL330 has different queues + * for loads and stores so this is already known at the top level where it + * matters. + * + * Queue works as FIFO for instructions with equivalent tags, but can issue + * instructions with different tags in arbitrary order. SEQN field attached to + * each instruction helps to achieve this. For each TAG queue contains + * instructions with consecutive SEQN values ranging from LO_SEQN[TAG] to + * HI_SEQN[TAG]-1 inclusive. SEQN is 8-bit unsigned integer, so SEQN=255 is + * followed by SEQN=0. + * + * Z bit indicates that zeroes should be stored. No MFIFO fetches are performed + * in this case. + */ + +static void pl330_queue_reset(PL330Queue *s) +{ + int i; + + for (i = 0; i < s->queue_size; i++) { + s->queue[i].tag = PL330_UNTAGGED; + } +} + +/* Initialize queue */ +static void pl330_queue_init(PL330Queue *s, int size, PL330State *parent) +{ + s->parent = parent; + s->queue = g_new0(PL330QueueEntry, size); + s->queue_size = size; +} + +/* Returns pointer to an empty slot or NULL if queue is full */ +static PL330QueueEntry *pl330_queue_find_empty(PL330Queue *s) +{ + int i; + + for (i = 0; i < s->queue_size; i++) { + if (s->queue[i].tag == PL330_UNTAGGED) { + return &s->queue[i]; + } + } + return NULL; +} + +/* Put instruction in queue. + * Return value: + * - zero - OK + * - non-zero - queue is full + */ + +static int pl330_queue_put_insn(PL330Queue *s, uint32_t addr, + int len, int n, bool inc, bool z, uint8_t tag) +{ + PL330QueueEntry *entry = pl330_queue_find_empty(s); + + if (!entry) { + return 1; + } + entry->tag = tag; + entry->addr = addr; + entry->len = len; + entry->n = n; + entry->z = z; + entry->inc = inc; + entry->seqn = s->parent->hi_seqn[tag]; + s->parent->hi_seqn[tag]++; + return 0; +} + +/* Returns a pointer to queue slot containing instruction which satisfies + * following conditions: + * - it has valid tag value (not PL330_UNTAGGED) + * - if enforce_seq is set it has to be issuable without violating queue + * logic (see above) + * - if TAG argument is not PL330_UNTAGGED this instruction has tag value + * equivalent to the argument TAG value. + * If such instruction cannot be found NULL is returned. + */ + +static PL330QueueEntry *pl330_queue_find_insn(PL330Queue *s, uint8_t tag, + bool enforce_seq) +{ + int i; + + for (i = 0; i < s->queue_size; i++) { + if (s->queue[i].tag != PL330_UNTAGGED) { + if ((!enforce_seq || + s->queue[i].seqn == s->parent->lo_seqn[s->queue[i].tag]) && + (s->queue[i].tag == tag || tag == PL330_UNTAGGED || + s->queue[i].z)) { + return &s->queue[i]; + } + } + } + return NULL; +} + +/* Removes instruction from queue. */ + +static inline void pl330_queue_remove_insn(PL330Queue *s, PL330QueueEntry *e) +{ + s->parent->lo_seqn[e->tag]++; + e->tag = PL330_UNTAGGED; +} + +/* Removes all instructions tagged with TAG from queue. */ + +static inline void pl330_queue_remove_tagged(PL330Queue *s, uint8_t tag) +{ + int i; + + for (i = 0; i < s->queue_size; i++) { + if (s->queue[i].tag == tag) { + s->queue[i].tag = PL330_UNTAGGED; + } + } +} + +/* DMA instruction execution engine */ + +/* Moves DMA channel to the FAULT state and updates it's status. */ + +static inline void pl330_fault(PL330Chan *ch, uint32_t flags) +{ + DB_PRINT("ch: %p, flags: %x\n", ch, flags); + ch->fault_type |= flags; + if (ch->state == pl330_chan_fault) { + return; + } + ch->state = pl330_chan_fault; + ch->parent->num_faulting++; + if (ch->parent->num_faulting == 1) { + DB_PRINT("abort interrupt raised\n"); + qemu_irq_raise(ch->parent->irq_abort); + } +} + +/* + * For information about instructions see PL330 Technical Reference Manual. + * + * Arguments: + * CH - channel executing the instruction + * OPCODE - opcode + * ARGS - array of 8-bit arguments + * LEN - number of elements in ARGS array + */ + +static void pl330_dmaaddh(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) +{ + uint16_t im = (((uint16_t)args[1]) << 8) | ((uint16_t)args[0]); + uint8_t ra = (opcode >> 1) & 1; + + if (ch->is_manager) { + pl330_fault(ch, PL330_FAULT_UNDEF_INSTR); + return; + } + if (ra) { + ch->dst += im; + } else { + ch->src += im; + } +} + +static void pl330_dmaend(PL330Chan *ch, uint8_t opcode, + uint8_t *args, int len) +{ + PL330State *s = ch->parent; + + if (ch->state == pl330_chan_executing && !ch->is_manager) { + /* Wait for all transfers to complete */ + if (pl330_fifo_has_tag(&s->fifo, ch->tag) || + pl330_queue_find_insn(&s->read_queue, ch->tag, false) != NULL || + pl330_queue_find_insn(&s->write_queue, ch->tag, false) != NULL) { + + ch->stall = 1; + return; + } + } + DB_PRINT("DMA ending!\n"); + pl330_fifo_tagged_remove(&s->fifo, ch->tag); + pl330_queue_remove_tagged(&s->read_queue, ch->tag); + pl330_queue_remove_tagged(&s->write_queue, ch->tag); + ch->state = pl330_chan_stopped; +} + +static void pl330_dmaflushp(PL330Chan *ch, uint8_t opcode, + uint8_t *args, int len) +{ + uint8_t periph_id; + + if (args[0] & 7) { + pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); + return; + } + periph_id = (args[0] >> 3) & 0x1f; + if (periph_id >= ch->parent->num_periph_req) { + pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); + return; + } + if (ch->ns && !(ch->parent->cfg[CFG_PNS] & (1 << periph_id))) { + pl330_fault(ch, PL330_FAULT_CH_PERIPH_ERR); + return; + } + /* Do nothing */ +} + +static void pl330_dmago(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) +{ + uint8_t chan_id; + uint8_t ns; + uint32_t pc; + PL330Chan *s; + + DB_PRINT("\n"); + + if (!ch->is_manager) { + pl330_fault(ch, PL330_FAULT_UNDEF_INSTR); + return; + } + ns = !!(opcode & 2); + chan_id = args[0] & 7; + if ((args[0] >> 3)) { + pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); + return; + } + if (chan_id >= ch->parent->num_chnls) { + pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); + return; + } + pc = (((uint32_t)args[4]) << 24) | (((uint32_t)args[3]) << 16) | + (((uint32_t)args[2]) << 8) | (((uint32_t)args[1])); + if (ch->parent->chan[chan_id].state != pl330_chan_stopped) { + pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); + return; + } + if (ch->ns && !ns) { + pl330_fault(ch, PL330_FAULT_DMAGO_ERR); + return; + } + s = &ch->parent->chan[chan_id]; + s->ns = ns; + s->pc = pc; + s->state = pl330_chan_executing; +} + +static void pl330_dmald(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) +{ + uint8_t bs = opcode & 3; + uint32_t size, num; + bool inc; + + if (bs == 2) { + pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); + return; + } + if ((bs == 1 && ch->request_flag == PL330_BURST) || + (bs == 3 && ch->request_flag == PL330_SINGLE)) { + /* Perform NOP */ + return; + } + if (bs == 1 && ch->request_flag == PL330_SINGLE) { + num = 1; + } else { + num = ((ch->control >> 4) & 0xf) + 1; + } + size = (uint32_t)1 << ((ch->control >> 1) & 0x7); + inc = !!(ch->control & 1); + ch->stall = pl330_queue_put_insn(&ch->parent->read_queue, ch->src, + size, num, inc, 0, ch->tag); + if (!ch->stall) { + DB_PRINT("channel:%d address:%08x size:%d num:%d %c\n", + ch->tag, ch->src, size, num, inc ? 'Y' : 'N'); + ch->src += inc ? size * num - (ch->src & (size - 1)) : 0; + } +} + +static void pl330_dmaldp(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) +{ + uint8_t periph_id; + + if (args[0] & 7) { + pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); + return; + } + periph_id = (args[0] >> 3) & 0x1f; + if (periph_id >= ch->parent->num_periph_req) { + pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); + return; + } + if (ch->ns && !(ch->parent->cfg[CFG_PNS] & (1 << periph_id))) { + pl330_fault(ch, PL330_FAULT_CH_PERIPH_ERR); + return; + } + pl330_dmald(ch, opcode, args, len); +} + +static void pl330_dmalp(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) +{ + uint8_t lc = (opcode & 2) >> 1; + + ch->lc[lc] = args[0]; +} + +static void pl330_dmakill(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) +{ + if (ch->state == pl330_chan_fault || + ch->state == pl330_chan_fault_completing) { + /* This is the only way for a channel to leave the faulting state */ + ch->fault_type = 0; + ch->parent->num_faulting--; + if (ch->parent->num_faulting == 0) { + DB_PRINT("abort interrupt lowered\n"); + qemu_irq_lower(ch->parent->irq_abort); + } + } + ch->state = pl330_chan_killing; + pl330_fifo_tagged_remove(&ch->parent->fifo, ch->tag); + pl330_queue_remove_tagged(&ch->parent->read_queue, ch->tag); + pl330_queue_remove_tagged(&ch->parent->write_queue, ch->tag); + ch->state = pl330_chan_stopped; +} + +static void pl330_dmalpend(PL330Chan *ch, uint8_t opcode, + uint8_t *args, int len) +{ + uint8_t nf = (opcode & 0x10) >> 4; + uint8_t bs = opcode & 3; + uint8_t lc = (opcode & 4) >> 2; + + if (bs == 2) { + pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); + return; + } + if ((bs == 1 && ch->request_flag == PL330_BURST) || + (bs == 3 && ch->request_flag == PL330_SINGLE)) { + /* Perform NOP */ + return; + } + if (!nf || ch->lc[lc]) { + if (nf) { + ch->lc[lc]--; + } + DB_PRINT("loop reiteration\n"); + ch->pc -= args[0]; + ch->pc -= len + 1; + /* "ch->pc -= args[0] + len + 1" is incorrect when args[0] == 256 */ + } else { + DB_PRINT("loop fallthrough\n"); + } +} + + +static void pl330_dmamov(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) +{ + uint8_t rd = args[0] & 7; + uint32_t im; + + if ((args[0] >> 3)) { + pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); + return; + } + im = (((uint32_t)args[4]) << 24) | (((uint32_t)args[3]) << 16) | + (((uint32_t)args[2]) << 8) | (((uint32_t)args[1])); + switch (rd) { + case 0: + ch->src = im; + break; + case 1: + ch->control = im; + break; + case 2: + ch->dst = im; + break; + default: + pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); + return; + } +} + +static void pl330_dmanop(PL330Chan *ch, uint8_t opcode, + uint8_t *args, int len) +{ + /* NOP is NOP. */ +} + +static void pl330_dmarmb(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) +{ + if (pl330_queue_find_insn(&ch->parent->read_queue, ch->tag, false)) { + ch->state = pl330_chan_at_barrier; + ch->stall = 1; + return; + } else { + ch->state = pl330_chan_executing; + } +} + +static void pl330_dmasev(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) +{ + uint8_t ev_id; + + if (args[0] & 7) { + pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); + return; + } + ev_id = (args[0] >> 3) & 0x1f; + if (ev_id >= ch->parent->num_events) { + pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); + return; + } + if (ch->ns && !(ch->parent->cfg[CFG_INS] & (1 << ev_id))) { + pl330_fault(ch, PL330_FAULT_EVENT_ERR); + return; + } + if (ch->parent->inten & (1 << ev_id)) { + ch->parent->int_status |= (1 << ev_id); + DB_PRINT("event interrupt raised %d\n", ev_id); + qemu_irq_raise(ch->parent->irq[ev_id]); + } else { + ch->parent->ev_status |= (1 << ev_id); + } +} + +static void pl330_dmast(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) +{ + uint8_t bs = opcode & 3; + uint32_t size, num; + bool inc; + + if (bs == 2) { + pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); + return; + } + if ((bs == 1 && ch->request_flag == PL330_BURST) || + (bs == 3 && ch->request_flag == PL330_SINGLE)) { + /* Perform NOP */ + return; + } + num = ((ch->control >> 18) & 0xf) + 1; + size = (uint32_t)1 << ((ch->control >> 15) & 0x7); + inc = !!((ch->control >> 14) & 1); + ch->stall = pl330_queue_put_insn(&ch->parent->write_queue, ch->dst, + size, num, inc, 0, ch->tag); + if (!ch->stall) { + DB_PRINT("channel:%d address:%08x size:%d num:%d %c\n", + ch->tag, ch->dst, size, num, inc ? 'Y' : 'N'); + ch->dst += inc ? size * num - (ch->dst & (size - 1)) : 0; + } +} + +static void pl330_dmastp(PL330Chan *ch, uint8_t opcode, + uint8_t *args, int len) +{ + uint8_t periph_id; + + if (args[0] & 7) { + pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); + return; + } + periph_id = (args[0] >> 3) & 0x1f; + if (periph_id >= ch->parent->num_periph_req) { + pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); + return; + } + if (ch->ns && !(ch->parent->cfg[CFG_PNS] & (1 << periph_id))) { + pl330_fault(ch, PL330_FAULT_CH_PERIPH_ERR); + return; + } + pl330_dmast(ch, opcode, args, len); +} + +static void pl330_dmastz(PL330Chan *ch, uint8_t opcode, + uint8_t *args, int len) +{ + uint32_t size, num; + bool inc; + + num = ((ch->control >> 18) & 0xf) + 1; + size = (uint32_t)1 << ((ch->control >> 15) & 0x7); + inc = !!((ch->control >> 14) & 1); + ch->stall = pl330_queue_put_insn(&ch->parent->write_queue, ch->dst, + size, num, inc, 1, ch->tag); + if (inc) { + ch->dst += size * num; + } +} + +static void pl330_dmawfe(PL330Chan *ch, uint8_t opcode, + uint8_t *args, int len) +{ + uint8_t ev_id; + int i; + + if (args[0] & 5) { + pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); + return; + } + ev_id = (args[0] >> 3) & 0x1f; + if (ev_id >= ch->parent->num_events) { + pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); + return; + } + if (ch->ns && !(ch->parent->cfg[CFG_INS] & (1 << ev_id))) { + pl330_fault(ch, PL330_FAULT_EVENT_ERR); + return; + } + ch->wakeup = ev_id; + ch->state = pl330_chan_waiting_event; + if (~ch->parent->inten & ch->parent->ev_status & 1 << ev_id) { + ch->state = pl330_chan_executing; + /* If anyone else is currently waiting on the same event, let them + * clear the ev_status so they pick up event as well + */ + for (i = 0; i < ch->parent->num_chnls; ++i) { + PL330Chan *peer = &ch->parent->chan[i]; + if (peer->state == pl330_chan_waiting_event && + peer->wakeup == ev_id) { + return; + } + } + ch->parent->ev_status &= ~(1 << ev_id); + } else { + ch->stall = 1; + } +} + +static void pl330_dmawfp(PL330Chan *ch, uint8_t opcode, + uint8_t *args, int len) +{ + uint8_t bs = opcode & 3; + uint8_t periph_id; + + if (args[0] & 7) { + pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); + return; + } + periph_id = (args[0] >> 3) & 0x1f; + if (periph_id >= ch->parent->num_periph_req) { + pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); + return; + } + if (ch->ns && !(ch->parent->cfg[CFG_PNS] & (1 << periph_id))) { + pl330_fault(ch, PL330_FAULT_CH_PERIPH_ERR); + return; + } + switch (bs) { + case 0: /* S */ + ch->request_flag = PL330_SINGLE; + ch->wfp_sbp = 0; + break; + case 1: /* P */ + ch->request_flag = PL330_BURST; + ch->wfp_sbp = 2; + break; + case 2: /* B */ + ch->request_flag = PL330_BURST; + ch->wfp_sbp = 1; + break; + default: + pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); + return; + } + + if (ch->parent->periph_busy[periph_id]) { + ch->state = pl330_chan_waiting_periph; + ch->stall = 1; + } else if (ch->state == pl330_chan_waiting_periph) { + ch->state = pl330_chan_executing; + } +} + +static void pl330_dmawmb(PL330Chan *ch, uint8_t opcode, + uint8_t *args, int len) +{ + if (pl330_queue_find_insn(&ch->parent->write_queue, ch->tag, false)) { + ch->state = pl330_chan_at_barrier; + ch->stall = 1; + return; + } else { + ch->state = pl330_chan_executing; + } +} + +/* NULL terminated array of the instruction descriptions. */ +static const PL330InsnDesc insn_desc[] = { + { .opcode = 0x54, .opmask = 0xFD, .size = 3, .exec = pl330_dmaaddh, }, + { .opcode = 0x00, .opmask = 0xFF, .size = 1, .exec = pl330_dmaend, }, + { .opcode = 0x35, .opmask = 0xFF, .size = 2, .exec = pl330_dmaflushp, }, + { .opcode = 0xA0, .opmask = 0xFD, .size = 6, .exec = pl330_dmago, }, + { .opcode = 0x04, .opmask = 0xFC, .size = 1, .exec = pl330_dmald, }, + { .opcode = 0x25, .opmask = 0xFD, .size = 2, .exec = pl330_dmaldp, }, + { .opcode = 0x20, .opmask = 0xFD, .size = 2, .exec = pl330_dmalp, }, + /* dmastp must be before dmalpend in this list, because their maps + * are overlapping + */ + { .opcode = 0x29, .opmask = 0xFD, .size = 2, .exec = pl330_dmastp, }, + { .opcode = 0x28, .opmask = 0xE8, .size = 2, .exec = pl330_dmalpend, }, + { .opcode = 0x01, .opmask = 0xFF, .size = 1, .exec = pl330_dmakill, }, + { .opcode = 0xBC, .opmask = 0xFF, .size = 6, .exec = pl330_dmamov, }, + { .opcode = 0x18, .opmask = 0xFF, .size = 1, .exec = pl330_dmanop, }, + { .opcode = 0x12, .opmask = 0xFF, .size = 1, .exec = pl330_dmarmb, }, + { .opcode = 0x34, .opmask = 0xFF, .size = 2, .exec = pl330_dmasev, }, + { .opcode = 0x08, .opmask = 0xFC, .size = 1, .exec = pl330_dmast, }, + { .opcode = 0x0C, .opmask = 0xFF, .size = 1, .exec = pl330_dmastz, }, + { .opcode = 0x36, .opmask = 0xFF, .size = 2, .exec = pl330_dmawfe, }, + { .opcode = 0x30, .opmask = 0xFC, .size = 2, .exec = pl330_dmawfp, }, + { .opcode = 0x13, .opmask = 0xFF, .size = 1, .exec = pl330_dmawmb, }, + { .opcode = 0x00, .opmask = 0x00, .size = 0, .exec = NULL, } +}; + +/* Instructions which can be issued via debug registers. */ +static const PL330InsnDesc debug_insn_desc[] = { + { .opcode = 0xA0, .opmask = 0xFD, .size = 6, .exec = pl330_dmago, }, + { .opcode = 0x01, .opmask = 0xFF, .size = 1, .exec = pl330_dmakill, }, + { .opcode = 0x34, .opmask = 0xFF, .size = 2, .exec = pl330_dmasev, }, + { .opcode = 0x00, .opmask = 0x00, .size = 0, .exec = NULL, } +}; + +static inline const PL330InsnDesc *pl330_fetch_insn(PL330Chan *ch) +{ + uint8_t opcode; + int i; + + dma_memory_read(&dma_context_memory, ch->pc, &opcode, 1); + for (i = 0; insn_desc[i].size; i++) { + if ((opcode & insn_desc[i].opmask) == insn_desc[i].opcode) { + return &insn_desc[i]; + } + } + return NULL; +} + +static inline void pl330_exec_insn(PL330Chan *ch, const PL330InsnDesc *insn) +{ + uint8_t buf[PL330_INSN_MAXSIZE]; + + assert(insn->size <= PL330_INSN_MAXSIZE); + dma_memory_read(&dma_context_memory, ch->pc, buf, insn->size); + insn->exec(ch, buf[0], &buf[1], insn->size - 1); +} + +static inline void pl330_update_pc(PL330Chan *ch, + const PL330InsnDesc *insn) +{ + ch->pc += insn->size; +} + +/* Try to execute current instruction in channel CH. Number of executed + instructions is returned (0 or 1). */ +static int pl330_chan_exec(PL330Chan *ch) +{ + const PL330InsnDesc *insn; + + if (ch->state != pl330_chan_executing && + ch->state != pl330_chan_waiting_periph && + ch->state != pl330_chan_at_barrier && + ch->state != pl330_chan_waiting_event) { + DB_PRINT("%d\n", ch->state); + return 0; + } + ch->stall = 0; + insn = pl330_fetch_insn(ch); + if (!insn) { + DB_PRINT("pl330 undefined instruction\n"); + pl330_fault(ch, PL330_FAULT_UNDEF_INSTR); + return 0; + } + pl330_exec_insn(ch, insn); + if (!ch->stall) { + pl330_update_pc(ch, insn); + ch->watchdog_timer = 0; + return 1; + /* WDT only active in exec state */ + } else if (ch->state == pl330_chan_executing) { + ch->watchdog_timer++; + if (ch->watchdog_timer >= PL330_WATCHDOG_LIMIT) { + pl330_fault(ch, PL330_FAULT_LOCKUP_ERR); + } + } + return 0; +} + +/* Try to execute 1 instruction in each channel, one instruction from read + queue and one instruction from write queue. Number of successfully executed + instructions is returned. */ +static int pl330_exec_cycle(PL330Chan *channel) +{ + PL330State *s = channel->parent; + PL330QueueEntry *q; + int i; + int num_exec = 0; + int fifo_res = 0; + uint8_t buf[PL330_MAX_BURST_LEN]; + + /* Execute one instruction in each channel */ + num_exec += pl330_chan_exec(channel); + + /* Execute one instruction from read queue */ + q = pl330_queue_find_insn(&s->read_queue, PL330_UNTAGGED, true); + if (q != NULL && q->len <= pl330_fifo_num_free(&s->fifo)) { + int len = q->len - (q->addr & (q->len - 1)); + + dma_memory_read(&dma_context_memory, q->addr, buf, len); + if (PL330_ERR_DEBUG > 1) { + DB_PRINT("PL330 read from memory @%08x (size = %08x):\n", + q->addr, len); + hexdump((char *)buf, stderr, "", len); + } + fifo_res = pl330_fifo_push(&s->fifo, buf, len, q->tag); + if (fifo_res == PL330_FIFO_OK) { + if (q->inc) { + q->addr += len; + } + q->n--; + if (!q->n) { + pl330_queue_remove_insn(&s->read_queue, q); + } + num_exec++; + } + } + + /* Execute one instruction from write queue. */ + q = pl330_queue_find_insn(&s->write_queue, pl330_fifo_tag(&s->fifo), true); + if (q != NULL) { + int len = q->len - (q->addr & (q->len - 1)); + + if (q->z) { + for (i = 0; i < len; i++) { + buf[i] = 0; + } + } else { + fifo_res = pl330_fifo_get(&s->fifo, buf, len, q->tag); + } + if (fifo_res == PL330_FIFO_OK || q->z) { + dma_memory_write(&dma_context_memory, q->addr, buf, len); + if (PL330_ERR_DEBUG > 1) { + DB_PRINT("PL330 read from memory @%08x (size = %08x):\n", + q->addr, len); + hexdump((char *)buf, stderr, "", len); + } + if (q->inc) { + q->addr += len; + } + num_exec++; + } else if (fifo_res == PL330_FIFO_STALL) { + pl330_fault(&channel->parent->chan[q->tag], + PL330_FAULT_FIFOEMPTY_ERR); + } + q->n--; + if (!q->n) { + pl330_queue_remove_insn(&s->write_queue, q); + } + } + + return num_exec; +} + +static int pl330_exec_channel(PL330Chan *channel) +{ + int insr_exec = 0; + + /* TODO: Is it all right to execute everything or should we do per-cycle + simulation? */ + while (pl330_exec_cycle(channel)) { + insr_exec++; + } + + /* Detect deadlock */ + if (channel->state == pl330_chan_executing) { + pl330_fault(channel, PL330_FAULT_LOCKUP_ERR); + } + /* Situation when one of the queues has deadlocked but all channels + * have finished their programs should be impossible. + */ + + return insr_exec; +} + +static inline void pl330_exec(PL330State *s) +{ + DB_PRINT("\n"); + int i, insr_exec; + do { + insr_exec = pl330_exec_channel(&s->manager); + + for (i = 0; i < s->num_chnls; i++) { + insr_exec += pl330_exec_channel(&s->chan[i]); + } + } while (insr_exec); +} + +static void pl330_exec_cycle_timer(void *opaque) +{ + PL330State *s = (PL330State *)opaque; + pl330_exec(s); +} + +/* Stop or restore dma operations */ + +static void pl330_dma_stop_irq(void *opaque, int irq, int level) +{ + PL330State *s = (PL330State *)opaque; + + if (s->periph_busy[irq] != level) { + s->periph_busy[irq] = level; + qemu_mod_timer(s->timer, qemu_get_clock_ns(vm_clock)); + } +} + +static void pl330_debug_exec(PL330State *s) +{ + uint8_t args[5]; + uint8_t opcode; + uint8_t chan_id; + int i; + PL330Chan *ch; + const PL330InsnDesc *insn; + + s->debug_status = 1; + chan_id = (s->dbg[0] >> 8) & 0x07; + opcode = (s->dbg[0] >> 16) & 0xff; + args[0] = (s->dbg[0] >> 24) & 0xff; + args[1] = (s->dbg[1] >> 0) & 0xff; + args[2] = (s->dbg[1] >> 8) & 0xff; + args[3] = (s->dbg[1] >> 16) & 0xff; + args[4] = (s->dbg[1] >> 24) & 0xff; + DB_PRINT("chan id: %d\n", chan_id); + if (s->dbg[0] & 1) { + ch = &s->chan[chan_id]; + } else { + ch = &s->manager; + } + insn = NULL; + for (i = 0; debug_insn_desc[i].size; i++) { + if ((opcode & debug_insn_desc[i].opmask) == debug_insn_desc[i].opcode) { + insn = &debug_insn_desc[i]; + } + } + if (!insn) { + pl330_fault(ch, PL330_FAULT_UNDEF_INSTR | PL330_FAULT_DBG_INSTR); + return ; + } + ch->stall = 0; + insn->exec(ch, opcode, args, insn->size - 1); + if (ch->fault_type) { + ch->fault_type |= PL330_FAULT_DBG_INSTR; + } + if (ch->stall) { + qemu_log_mask(LOG_UNIMP, "pl330: stall of debug instruction not " + "implemented\n"); + } + s->debug_status = 0; +} + +/* IOMEM mapped registers */ + +static void pl330_iomem_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + PL330State *s = (PL330State *) opaque; + uint32_t i; + + DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, (unsigned)value); + + switch (offset) { + case PL330_REG_INTEN: + s->inten = value; + break; + case PL330_REG_INTCLR: + for (i = 0; i < s->num_events; i++) { + if (s->int_status & s->inten & value & (1 << i)) { + DB_PRINT("event interrupt lowered %d\n", i); + qemu_irq_lower(s->irq[i]); + } + } + s->ev_status &= ~(value & s->inten); + s->int_status &= ~(value & s->inten); + break; + case PL330_REG_DBGCMD: + if ((value & 3) == 0) { + pl330_debug_exec(s); + pl330_exec(s); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "pl330: write of illegal value %u " + "for offset " TARGET_FMT_plx "\n", (unsigned)value, + offset); + } + break; + case PL330_REG_DBGINST0: + DB_PRINT("s->dbg[0] = %08x\n", (unsigned)value); + s->dbg[0] = value; + break; + case PL330_REG_DBGINST1: + DB_PRINT("s->dbg[1] = %08x\n", (unsigned)value); + s->dbg[1] = value; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad write offset " TARGET_FMT_plx + "\n", offset); + break; + } +} + +static inline uint32_t pl330_iomem_read_imp(void *opaque, + hwaddr offset) +{ + PL330State *s = (PL330State *)opaque; + int chan_id; + int i; + uint32_t res; + + if (offset >= PL330_REG_PERIPH_ID && offset < PL330_REG_PERIPH_ID + 32) { + return pl330_id[(offset - PL330_REG_PERIPH_ID) >> 2]; + } + if (offset >= PL330_REG_CR0_BASE && offset < PL330_REG_CR0_BASE + 24) { + return s->cfg[(offset - PL330_REG_CR0_BASE) >> 2]; + } + if (offset >= PL330_REG_CHANCTRL && offset < PL330_REG_DBGSTATUS) { + offset -= PL330_REG_CHANCTRL; + chan_id = offset >> 5; + if (chan_id >= s->num_chnls) { + qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset " + TARGET_FMT_plx "\n", offset); + return 0; + } + switch (offset & 0x1f) { + case 0x00: + return s->chan[chan_id].src; + case 0x04: + return s->chan[chan_id].dst; + case 0x08: + return s->chan[chan_id].control; + case 0x0C: + return s->chan[chan_id].lc[0]; + case 0x10: + return s->chan[chan_id].lc[1]; + default: + qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset " + TARGET_FMT_plx "\n", offset); + return 0; + } + } + if (offset >= PL330_REG_CSR_BASE && offset < 0x400) { + offset -= PL330_REG_CSR_BASE; + chan_id = offset >> 3; + if (chan_id >= s->num_chnls) { + qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset " + TARGET_FMT_plx "\n", offset); + return 0; + } + switch ((offset >> 2) & 1) { + case 0x0: + res = (s->chan[chan_id].ns << 21) | + (s->chan[chan_id].wakeup << 4) | + (s->chan[chan_id].state) | + (s->chan[chan_id].wfp_sbp << 14); + return res; + case 0x1: + return s->chan[chan_id].pc; + default: + qemu_log_mask(LOG_GUEST_ERROR, "pl330: read error\n"); + return 0; + } + } + if (offset >= PL330_REG_FTR_BASE && offset < 0x100) { + offset -= PL330_REG_FTR_BASE; + chan_id = offset >> 2; + if (chan_id >= s->num_chnls) { + qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset " + TARGET_FMT_plx "\n", offset); + return 0; + } + return s->chan[chan_id].fault_type; + } + switch (offset) { + case PL330_REG_DSR: + return (s->manager.ns << 9) | (s->manager.wakeup << 4) | + (s->manager.state & 0xf); + case PL330_REG_DPC: + return s->manager.pc; + case PL330_REG_INTEN: + return s->inten; + case PL330_REG_INT_EVENT_RIS: + return s->ev_status; + case PL330_REG_INTMIS: + return s->int_status; + case PL330_REG_INTCLR: + /* Documentation says that we can't read this register + * but linux kernel does it + */ + return 0; + case PL330_REG_FSRD: + return s->manager.state ? 1 : 0; + case PL330_REG_FSRC: + res = 0; + for (i = 0; i < s->num_chnls; i++) { + if (s->chan[i].state == pl330_chan_fault || + s->chan[i].state == pl330_chan_fault_completing) { + res |= 1 << i; + } + } + return res; + case PL330_REG_FTRD: + return s->manager.fault_type; + case PL330_REG_DBGSTATUS: + return s->debug_status; + default: + qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset " + TARGET_FMT_plx "\n", offset); + } + return 0; +} + +static uint64_t pl330_iomem_read(void *opaque, hwaddr offset, + unsigned size) +{ + int ret = pl330_iomem_read_imp(opaque, offset); + DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, ret); + return ret; +} + +static const MemoryRegionOps pl330_ops = { + .read = pl330_iomem_read, + .write = pl330_iomem_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +/* Controller logic and initialization */ + +static void pl330_chan_reset(PL330Chan *ch) +{ + ch->src = 0; + ch->dst = 0; + ch->pc = 0; + ch->state = pl330_chan_stopped; + ch->watchdog_timer = 0; + ch->stall = 0; + ch->control = 0; + ch->status = 0; + ch->fault_type = 0; +} + +static void pl330_reset(DeviceState *d) +{ + int i; + PL330State *s = PL330(d); + + s->inten = 0; + s->int_status = 0; + s->ev_status = 0; + s->debug_status = 0; + s->num_faulting = 0; + s->manager.ns = s->mgr_ns_at_rst; + pl330_fifo_reset(&s->fifo); + pl330_queue_reset(&s->read_queue); + pl330_queue_reset(&s->write_queue); + + for (i = 0; i < s->num_chnls; i++) { + pl330_chan_reset(&s->chan[i]); + } + for (i = 0; i < s->num_periph_req; i++) { + s->periph_busy[i] = 0; + } + + qemu_del_timer(s->timer); +} + +static void pl330_realize(DeviceState *dev, Error **errp) +{ + int i; + PL330State *s = PL330(dev); + + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq_abort); + memory_region_init_io(&s->iomem, &pl330_ops, s, "dma", PL330_IOMEM_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); + + s->timer = qemu_new_timer_ns(vm_clock, pl330_exec_cycle_timer, s); + + s->cfg[0] = (s->mgr_ns_at_rst ? 0x4 : 0) | + (s->num_periph_req > 0 ? 1 : 0) | + ((s->num_chnls - 1) & 0x7) << 4 | + ((s->num_periph_req - 1) & 0x1f) << 12 | + ((s->num_events - 1) & 0x1f) << 17; + + switch (s->i_cache_len) { + case (4): + s->cfg[1] |= 2; + break; + case (8): + s->cfg[1] |= 3; + break; + case (16): + s->cfg[1] |= 4; + break; + case (32): + s->cfg[1] |= 5; + break; + default: + error_setg(errp, "Bad value for i-cache_len property: %d\n", + s->i_cache_len); + return; + } + s->cfg[1] |= ((s->num_i_cache_lines - 1) & 0xf) << 4; + + s->chan = g_new0(PL330Chan, s->num_chnls); + s->hi_seqn = g_new0(uint8_t, s->num_chnls); + s->lo_seqn = g_new0(uint8_t, s->num_chnls); + for (i = 0; i < s->num_chnls; i++) { + s->chan[i].parent = s; + s->chan[i].tag = (uint8_t)i; + } + s->manager.parent = s; + s->manager.tag = s->num_chnls; + s->manager.is_manager = true; + + s->irq = g_new0(qemu_irq, s->num_events); + for (i = 0; i < s->num_events; i++) { + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[i]); + } + + qdev_init_gpio_in(dev, pl330_dma_stop_irq, PL330_PERIPH_NUM); + + switch (s->data_width) { + case (32): + s->cfg[CFG_CRD] |= 0x2; + break; + case (64): + s->cfg[CFG_CRD] |= 0x3; + break; + case (128): + s->cfg[CFG_CRD] |= 0x4; + break; + default: + error_setg(errp, "Bad value for data_width property: %d\n", + s->data_width); + return; + } + + s->cfg[CFG_CRD] |= ((s->wr_cap - 1) & 0x7) << 4 | + ((s->wr_q_dep - 1) & 0xf) << 8 | + ((s->rd_cap - 1) & 0x7) << 12 | + ((s->rd_q_dep - 1) & 0xf) << 16 | + ((s->data_buffer_dep - 1) & 0x1ff) << 20; + + pl330_queue_init(&s->read_queue, s->rd_q_dep, s); + pl330_queue_init(&s->write_queue, s->wr_q_dep, s); + pl330_fifo_init(&s->fifo, s->data_buffer_dep); +} + +static Property pl330_properties[] = { + /* CR0 */ + DEFINE_PROP_UINT32("num_chnls", PL330State, num_chnls, 8), + DEFINE_PROP_UINT8("num_periph_req", PL330State, num_periph_req, 4), + DEFINE_PROP_UINT8("num_events", PL330State, num_events, 16), + DEFINE_PROP_UINT8("mgr_ns_at_rst", PL330State, mgr_ns_at_rst, 0), + /* CR1 */ + DEFINE_PROP_UINT8("i-cache_len", PL330State, i_cache_len, 4), + DEFINE_PROP_UINT8("num_i-cache_lines", PL330State, num_i_cache_lines, 8), + /* CR2-4 */ + DEFINE_PROP_UINT32("boot_addr", PL330State, cfg[CFG_BOOT_ADDR], 0), + DEFINE_PROP_UINT32("INS", PL330State, cfg[CFG_INS], 0), + DEFINE_PROP_UINT32("PNS", PL330State, cfg[CFG_PNS], 0), + /* CRD */ + DEFINE_PROP_UINT8("data_width", PL330State, data_width, 64), + DEFINE_PROP_UINT8("wr_cap", PL330State, wr_cap, 8), + DEFINE_PROP_UINT8("wr_q_dep", PL330State, wr_q_dep, 16), + DEFINE_PROP_UINT8("rd_cap", PL330State, rd_cap, 8), + DEFINE_PROP_UINT8("rd_q_dep", PL330State, rd_q_dep, 16), + DEFINE_PROP_UINT16("data_buffer_dep", PL330State, data_buffer_dep, 256), + + DEFINE_PROP_END_OF_LIST(), +}; + +static void pl330_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = pl330_realize; + dc->reset = pl330_reset; + dc->props = pl330_properties; + dc->vmsd = &vmstate_pl330; +} + +static const TypeInfo pl330_type_info = { + .name = TYPE_PL330, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PL330State), + .class_init = pl330_class_init, +}; + +static void pl330_register_types(void) +{ + type_register_static(&pl330_type_info); +} + +type_init(pl330_register_types) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index f355a9bb84..7b2a11fbe4 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -629,7 +629,7 @@ static void ppc_spapr_reset(void) spapr->rtas_size); /* Set up the entry state */ - first_cpu_cpu = CPU(first_cpu); + first_cpu_cpu = ENV_GET_CPU(first_cpu); first_cpu->gpr[3] = spapr->fdt_addr; first_cpu->gpr[5] = 0; first_cpu_cpu->halted = 0; @@ -779,6 +779,11 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) spapr->htab_shift++; } + /* Set up Interrupt Controller before we create the VCPUs */ + spapr->icp = xics_system_init(smp_cpus * kvmppc_smt_threads() / smp_threads, + XICS_IRQS); + spapr->next_irq = XICS_IRQ_BASE; + /* init CPUs */ if (cpu_model == NULL) { cpu_model = kvm_enabled() ? "host" : "POWER7"; @@ -791,6 +796,8 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) } env = &cpu->env; + xics_cpu_setup(spapr->icp, cpu); + /* Set time-base frequency to 512 MHz */ cpu_ppc_tb_init(env, TIMEBASE_FREQ); @@ -830,11 +837,6 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) } g_free(filename); - - /* Set up Interrupt Controller */ - spapr->icp = xics_system_init(XICS_IRQS); - spapr->next_irq = XICS_IRQ_BASE; - /* Set up EPOW events infrastructure */ spapr_events_init(spapr); @@ -856,7 +858,7 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) /* Set up PCI */ spapr_pci_rtas_init(); - phb = spapr_create_phb(spapr, 0, "pci"); + phb = spapr_create_phb(spapr, 0); for (i = 0; i < nb_nics; i++) { NICInfo *nd = &nd_table[i]; diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index dd72743b52..22cfb7e674 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -3,39 +3,7 @@ #include "sysemu/sysemu.h" #include "helper_regs.h" #include "hw/spapr.h" - -#define HPTES_PER_GROUP 8 - -#define HPTE_V_SSIZE_SHIFT 62 -#define HPTE_V_AVPN_SHIFT 7 -#define HPTE_V_AVPN 0x3fffffffffffff80ULL -#define HPTE_V_AVPN_VAL(x) (((x) & HPTE_V_AVPN) >> HPTE_V_AVPN_SHIFT) -#define HPTE_V_COMPARE(x, y) (!(((x) ^ (y)) & 0xffffffffffffff80UL)) -#define HPTE_V_BOLTED 0x0000000000000010ULL -#define HPTE_V_LOCK 0x0000000000000008ULL -#define HPTE_V_LARGE 0x0000000000000004ULL -#define HPTE_V_SECONDARY 0x0000000000000002ULL -#define HPTE_V_VALID 0x0000000000000001ULL - -#define HPTE_R_PP0 0x8000000000000000ULL -#define HPTE_R_TS 0x4000000000000000ULL -#define HPTE_R_KEY_HI 0x3000000000000000ULL -#define HPTE_R_RPN_SHIFT 12 -#define HPTE_R_RPN 0x3ffffffffffff000ULL -#define HPTE_R_FLAGS 0x00000000000003ffULL -#define HPTE_R_PP 0x0000000000000003ULL -#define HPTE_R_N 0x0000000000000004ULL -#define HPTE_R_G 0x0000000000000008ULL -#define HPTE_R_M 0x0000000000000010ULL -#define HPTE_R_I 0x0000000000000020ULL -#define HPTE_R_W 0x0000000000000040ULL -#define HPTE_R_WIMG 0x0000000000000078ULL -#define HPTE_R_C 0x0000000000000080ULL -#define HPTE_R_R 0x0000000000000100ULL -#define HPTE_R_KEY_LO 0x0000000000000e00ULL - -#define HPTE_V_1TB_SEG 0x4000000000000000ULL -#define HPTE_V_VRMA_MASK 0x4001ffffff000000ULL +#include "mmu-hash64.h" static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r, target_ulong pte_index) @@ -44,17 +12,17 @@ static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r, rb = (v & ~0x7fULL) << 16; /* AVA field */ va_low = pte_index >> 3; - if (v & HPTE_V_SECONDARY) { + if (v & HPTE64_V_SECONDARY) { va_low = ~va_low; } /* xor vsid from AVA */ - if (!(v & HPTE_V_1TB_SEG)) { + if (!(v & HPTE64_V_1TB_SEG)) { va_low ^= v >> 12; } else { va_low ^= v >> 24; } va_low &= 0x7ff; - if (v & HPTE_V_LARGE) { + if (v & HPTE64_V_LARGE) { rb |= 1; /* L field */ #if 0 /* Disable that P7 specific bit for now */ if (r & 0xff000) { @@ -84,10 +52,10 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong page_shift = 12; target_ulong raddr; target_ulong i; - uint8_t *hpte; + hwaddr hpte; /* only handle 4k and 16M pages for now */ - if (pteh & HPTE_V_LARGE) { + if (pteh & HPTE64_V_LARGE) { #if 0 /* We don't support 64k pages yet */ if ((ptel & 0xf000) == 0x1000) { /* 64k page */ @@ -105,11 +73,11 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, } } - raddr = (ptel & HPTE_R_RPN) & ~((1ULL << page_shift) - 1); + raddr = (ptel & HPTE64_R_RPN) & ~((1ULL << page_shift) - 1); if (raddr < spapr->ram_limit) { /* Regular RAM - should have WIMG=0010 */ - if ((ptel & HPTE_R_WIMG) != HPTE_R_M) { + if ((ptel & HPTE64_R_WIMG) != HPTE64_R_M) { return H_PARAMETER; } } else { @@ -117,7 +85,7 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, /* FIXME: What WIMG combinations could be sensible for IO? * For now we allow WIMG=010x, but are there others? */ /* FIXME: Should we check against registered IO addresses? */ - if ((ptel & (HPTE_R_W | HPTE_R_I | HPTE_R_M)) != HPTE_R_I) { + if ((ptel & (HPTE64_R_W | HPTE64_R_I | HPTE64_R_M)) != HPTE64_R_I) { return H_PARAMETER; } } @@ -129,26 +97,26 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, } if (likely((flags & H_EXACT) == 0)) { pte_index &= ~7ULL; - hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64); + hpte = pte_index * HASH_PTE_SIZE_64; for (i = 0; ; ++i) { if (i == 8) { return H_PTEG_FULL; } - if ((ldq_p(hpte) & HPTE_V_VALID) == 0) { + if ((ppc_hash64_load_hpte0(env, hpte) & HPTE64_V_VALID) == 0) { break; } hpte += HASH_PTE_SIZE_64; } } else { i = 0; - hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64); - if (ldq_p(hpte) & HPTE_V_VALID) { + hpte = pte_index * HASH_PTE_SIZE_64; + if (ppc_hash64_load_hpte0(env, hpte) & HPTE64_V_VALID) { return H_PTEG_FULL; } } - stq_p(hpte + (HASH_PTE_SIZE_64/2), ptel); + ppc_hash64_store_hpte1(env, hpte, ptel); /* eieio(); FIXME: need some sort of barrier for smp? */ - stq_p(hpte, pteh); + ppc_hash64_store_hpte0(env, hpte, pteh); args[0] = pte_index + i; return H_SUCCESS; @@ -166,26 +134,26 @@ static target_ulong remove_hpte(CPUPPCState *env, target_ulong ptex, target_ulong flags, target_ulong *vp, target_ulong *rp) { - uint8_t *hpte; + hwaddr hpte; target_ulong v, r, rb; if ((ptex * HASH_PTE_SIZE_64) & ~env->htab_mask) { return REMOVE_PARM; } - hpte = env->external_htab + (ptex * HASH_PTE_SIZE_64); + hpte = ptex * HASH_PTE_SIZE_64; - v = ldq_p(hpte); - r = ldq_p(hpte + (HASH_PTE_SIZE_64/2)); + v = ppc_hash64_load_hpte0(env, hpte); + r = ppc_hash64_load_hpte1(env, hpte); - if ((v & HPTE_V_VALID) == 0 || + if ((v & HPTE64_V_VALID) == 0 || ((flags & H_AVPN) && (v & ~0x7fULL) != avpn) || ((flags & H_ANDCOND) && (v & avpn) != 0)) { return REMOVE_NOT_FOUND; } *vp = v; *rp = r; - stq_p(hpte, 0); + ppc_hash64_store_hpte0(env, hpte, 0); rb = compute_tlbie_rb(v, r, ptex); ppc_tlb_invalidate_one(env, rb); return REMOVE_SUCCESS; @@ -271,7 +239,7 @@ static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPREnvironment *spapr, switch (ret) { case REMOVE_SUCCESS: - *tsh |= (r & (HPTE_R_C | HPTE_R_R)) << 43; + *tsh |= (r & (HPTE64_R_C | HPTE64_R_R)) << 43; break; case REMOVE_PARM: @@ -292,34 +260,34 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong flags = args[0]; target_ulong pte_index = args[1]; target_ulong avpn = args[2]; - uint8_t *hpte; + hwaddr hpte; target_ulong v, r, rb; if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { return H_PARAMETER; } - hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64); + hpte = pte_index * HASH_PTE_SIZE_64; - v = ldq_p(hpte); - r = ldq_p(hpte + (HASH_PTE_SIZE_64/2)); + v = ppc_hash64_load_hpte0(env, hpte); + r = ppc_hash64_load_hpte1(env, hpte); - if ((v & HPTE_V_VALID) == 0 || + if ((v & HPTE64_V_VALID) == 0 || ((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) { return H_NOT_FOUND; } - r &= ~(HPTE_R_PP0 | HPTE_R_PP | HPTE_R_N | - HPTE_R_KEY_HI | HPTE_R_KEY_LO); - r |= (flags << 55) & HPTE_R_PP0; - r |= (flags << 48) & HPTE_R_KEY_HI; - r |= flags & (HPTE_R_PP | HPTE_R_N | HPTE_R_KEY_LO); + r &= ~(HPTE64_R_PP0 | HPTE64_R_PP | HPTE64_R_N | + HPTE64_R_KEY_HI | HPTE64_R_KEY_LO); + r |= (flags << 55) & HPTE64_R_PP0; + r |= (flags << 48) & HPTE64_R_KEY_HI; + r |= flags & (HPTE64_R_PP | HPTE64_R_N | HPTE64_R_KEY_LO); rb = compute_tlbie_rb(v, r, pte_index); - stq_p(hpte, v & ~HPTE_V_VALID); + ppc_hash64_store_hpte0(env, hpte, v & ~HPTE64_V_VALID); ppc_tlb_invalidate_one(env, rb); - stq_p(hpte + (HASH_PTE_SIZE_64/2), r); + ppc_hash64_store_hpte1(env, hpte, r); /* Don't need a memory barrier, due to qemu's global lock */ - stq_p(hpte, v); + ppc_hash64_store_hpte0(env, hpte, v); return H_SUCCESS; } diff --git a/hw/ppc/xics.c b/hw/ppc/xics.c index c3ef12fff4..374da5bbfd 100644 --- a/hw/ppc/xics.c +++ b/hw/ppc/xics.c @@ -521,45 +521,38 @@ static void xics_reset(void *opaque) } } -struct icp_state *xics_system_init(int nr_irqs) +void xics_cpu_setup(struct icp_state *icp, PowerPCCPU *cpu) { - CPUPPCState *env; - CPUState *cpu; - int max_server_num; - struct icp_state *icp; - struct ics_state *ics; + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + struct icp_server_state *ss = &icp->ss[cs->cpu_index]; - max_server_num = -1; - for (env = first_cpu; env != NULL; env = env->next_cpu) { - cpu = CPU(ppc_env_get_cpu(env)); - if (cpu->cpu_index > max_server_num) { - max_server_num = cpu->cpu_index; - } - } + assert(cs->cpu_index < icp->nr_servers); - icp = g_malloc0(sizeof(*icp)); - icp->nr_servers = max_server_num + 1; - icp->ss = g_malloc0(icp->nr_servers*sizeof(struct icp_server_state)); + switch (PPC_INPUT(env)) { + case PPC_FLAGS_INPUT_POWER7: + ss->output = env->irq_inputs[POWER7_INPUT_INT]; + break; - for (env = first_cpu; env != NULL; env = env->next_cpu) { - cpu = CPU(ppc_env_get_cpu(env)); - struct icp_server_state *ss = &icp->ss[cpu->cpu_index]; + case PPC_FLAGS_INPUT_970: + ss->output = env->irq_inputs[PPC970_INPUT_INT]; + break; - switch (PPC_INPUT(env)) { - case PPC_FLAGS_INPUT_POWER7: - ss->output = env->irq_inputs[POWER7_INPUT_INT]; - break; + default: + fprintf(stderr, "XICS interrupt controller does not support this CPU " + "bus model\n"); + abort(); + } +} - case PPC_FLAGS_INPUT_970: - ss->output = env->irq_inputs[PPC970_INPUT_INT]; - break; +struct icp_state *xics_system_init(int nr_servers, int nr_irqs) +{ + struct icp_state *icp; + struct ics_state *ics; - default: - hw_error("XICS interrupt model does not support this CPU bus " - "model\n"); - exit(1); - } - } + icp = g_malloc0(sizeof(*icp)); + icp->nr_servers = nr_servers; + icp->ss = g_malloc0(icp->nr_servers*sizeof(struct icp_server_state)); ics = g_malloc0(sizeof(*ics)); ics->nr_irqs = nr_irqs; diff --git a/hw/pxa2xx_lcd.c b/hw/pxa2xx_lcd.c index 6484d27de1..f2b0c93661 100644 --- a/hw/pxa2xx_lcd.c +++ b/hw/pxa2xx_lcd.c @@ -39,7 +39,7 @@ struct PXA2xxLCDState { int irqlevel; int invalidated; - DisplayState *ds; + QemuConsole *con; drawfn *line_fn[2]; int dest_width; int xres, yres; @@ -579,6 +579,7 @@ static const MemoryRegionOps pxa2xx_lcdc_ops = { /* Load new palette for a given DMA channel, convert to internal format */ static void pxa2xx_palette_parse(PXA2xxLCDState *s, int ch, int bpp) { + DisplaySurface *surface = qemu_console_surface(s->con); int i, n, format, r, g, b, alpha; uint32_t *dest; uint8_t *src; @@ -652,7 +653,7 @@ static void pxa2xx_palette_parse(PXA2xxLCDState *s, int ch, int bpp) src += 4; break; } - switch (ds_get_bits_per_pixel(s->ds)) { + switch (surface_bits_per_pixel(surface)) { case 8: *dest = rgb_to_pixel8(r, g, b) | alpha; break; @@ -676,6 +677,7 @@ static void pxa2xx_palette_parse(PXA2xxLCDState *s, int ch, int bpp) static void pxa2xx_lcdc_dma0_redraw_rot0(PXA2xxLCDState *s, hwaddr addr, int *miny, int *maxy) { + DisplaySurface *surface = qemu_console_surface(s->con); int src_width, dest_width; drawfn fn = NULL; if (s->dest_width) @@ -693,7 +695,7 @@ static void pxa2xx_lcdc_dma0_redraw_rot0(PXA2xxLCDState *s, dest_width = s->xres * s->dest_width; *miny = 0; - framebuffer_update_display(s->ds, s->sysmem, + framebuffer_update_display(surface, s->sysmem, addr, s->xres, s->yres, src_width, dest_width, s->dest_width, s->invalidated, @@ -703,6 +705,7 @@ static void pxa2xx_lcdc_dma0_redraw_rot0(PXA2xxLCDState *s, static void pxa2xx_lcdc_dma0_redraw_rot90(PXA2xxLCDState *s, hwaddr addr, int *miny, int *maxy) { + DisplaySurface *surface = qemu_console_surface(s->con); int src_width, dest_width; drawfn fn = NULL; if (s->dest_width) @@ -720,7 +723,7 @@ static void pxa2xx_lcdc_dma0_redraw_rot90(PXA2xxLCDState *s, dest_width = s->yres * s->dest_width; *miny = 0; - framebuffer_update_display(s->ds, s->sysmem, + framebuffer_update_display(surface, s->sysmem, addr, s->xres, s->yres, src_width, s->dest_width, -dest_width, s->invalidated, @@ -731,6 +734,7 @@ static void pxa2xx_lcdc_dma0_redraw_rot90(PXA2xxLCDState *s, static void pxa2xx_lcdc_dma0_redraw_rot180(PXA2xxLCDState *s, hwaddr addr, int *miny, int *maxy) { + DisplaySurface *surface = qemu_console_surface(s->con); int src_width, dest_width; drawfn fn = NULL; if (s->dest_width) { @@ -751,7 +755,7 @@ static void pxa2xx_lcdc_dma0_redraw_rot180(PXA2xxLCDState *s, dest_width = s->xres * s->dest_width; *miny = 0; - framebuffer_update_display(s->ds, s->sysmem, + framebuffer_update_display(surface, s->sysmem, addr, s->xres, s->yres, src_width, -dest_width, -s->dest_width, s->invalidated, @@ -761,6 +765,7 @@ static void pxa2xx_lcdc_dma0_redraw_rot180(PXA2xxLCDState *s, static void pxa2xx_lcdc_dma0_redraw_rot270(PXA2xxLCDState *s, hwaddr addr, int *miny, int *maxy) { + DisplaySurface *surface = qemu_console_surface(s->con); int src_width, dest_width; drawfn fn = NULL; if (s->dest_width) { @@ -781,7 +786,7 @@ static void pxa2xx_lcdc_dma0_redraw_rot270(PXA2xxLCDState *s, dest_width = s->yres * s->dest_width; *miny = 0; - framebuffer_update_display(s->ds, s->sysmem, + framebuffer_update_display(surface, s->sysmem, addr, s->xres, s->yres, src_width, -s->dest_width, dest_width, s->invalidated, @@ -800,9 +805,9 @@ static void pxa2xx_lcdc_resize(PXA2xxLCDState *s) if (width != s->xres || height != s->yres) { if (s->orientation == 90 || s->orientation == 270) { - qemu_console_resize(s->ds, height, width); + qemu_console_resize(s->con, height, width); } else { - qemu_console_resize(s->ds, width, height); + qemu_console_resize(s->con, width, height); } s->invalidated = 1; s->xres = width; @@ -871,20 +876,20 @@ static void pxa2xx_update_display(void *opaque) if (miny >= 0) { switch (s->orientation) { case 0: - dpy_gfx_update(s->ds, 0, miny, s->xres, maxy - miny + 1); + dpy_gfx_update(s->con, 0, miny, s->xres, maxy - miny + 1); break; case 90: - dpy_gfx_update(s->ds, miny, 0, maxy - miny + 1, s->xres); + dpy_gfx_update(s->con, miny, 0, maxy - miny + 1, s->xres); break; case 180: maxy = s->yres - maxy - 1; miny = s->yres - miny - 1; - dpy_gfx_update(s->ds, 0, maxy, s->xres, miny - maxy + 1); + dpy_gfx_update(s->con, 0, maxy, s->xres, miny - maxy + 1); break; case 270: maxy = s->yres - maxy - 1; miny = s->yres - miny - 1; - dpy_gfx_update(s->ds, maxy, 0, miny - maxy + 1, s->xres); + dpy_gfx_update(s->con, maxy, 0, miny - maxy + 1, s->xres); break; } } @@ -990,6 +995,7 @@ PXA2xxLCDState *pxa2xx_lcdc_init(MemoryRegion *sysmem, hwaddr base, qemu_irq irq) { PXA2xxLCDState *s; + DisplaySurface *surface; s = (PXA2xxLCDState *) g_malloc0(sizeof(PXA2xxLCDState)); s->invalidated = 1; @@ -1002,11 +1008,12 @@ PXA2xxLCDState *pxa2xx_lcdc_init(MemoryRegion *sysmem, "pxa2xx-lcd-controller", 0x00100000); memory_region_add_subregion(sysmem, base, &s->iomem); - s->ds = graphic_console_init(pxa2xx_update_display, - pxa2xx_invalidate_display, - NULL, NULL, s); + s->con = graphic_console_init(pxa2xx_update_display, + pxa2xx_invalidate_display, + NULL, NULL, s); + surface = qemu_console_surface(s->con); - switch (ds_get_bits_per_pixel(s->ds)) { + switch (surface_bits_per_pixel(surface)) { case 0: s->dest_width = 0; break; diff --git a/hw/qdev-addr.c b/hw/qdev-addr.c index 2398b4a37f..80a38bb017 100644 --- a/hw/qdev-addr.c +++ b/hw/qdev-addr.c @@ -42,7 +42,7 @@ static void set_taddr(Object *obj, Visitor *v, void *opaque, int64_t value; if (dev->realized) { - error_set(errp, QERR_PERMISSION_DENIED); + qdev_prop_set_after_realize(dev, name, errp); return; } diff --git a/hw/qdev-core.h b/hw/qdev-core.h index 2486f36853..547fbc7e0d 100644 --- a/hw/qdev-core.h +++ b/hw/qdev-core.h @@ -175,6 +175,9 @@ struct Property { uint8_t bitnr; uint8_t qtype; int64_t defval; + int arrayoffset; + PropertyInfo *arrayinfo; + int arrayfieldsize; }; struct PropertyInfo { diff --git a/hw/qdev-properties-system.c b/hw/qdev-properties-system.c index 87951444a1..28813d3978 100644 --- a/hw/qdev-properties-system.c +++ b/hw/qdev-properties-system.c @@ -43,7 +43,7 @@ static void set_pointer(Object *obj, Visitor *v, Property *prop, int ret; if (dev->realized) { - error_set(errp, QERR_PERMISSION_DENIED); + qdev_prop_set_after_realize(dev, name, errp); return; } @@ -287,7 +287,7 @@ static void set_vlan(Object *obj, Visitor *v, void *opaque, NetClientState *hubport; if (dev->realized) { - error_set(errp, QERR_PERMISSION_DENIED); + qdev_prop_set_after_realize(dev, name, errp); return; } diff --git a/hw/qdev-properties.c b/hw/qdev-properties.c index 0307a7830b..168c4663e9 100644 --- a/hw/qdev-properties.c +++ b/hw/qdev-properties.c @@ -7,6 +7,20 @@ #include "qapi/visitor.h" #include "char/char.h" +void qdev_prop_set_after_realize(DeviceState *dev, const char *name, + Error **errp) +{ + if (dev->id) { + error_setg(errp, "Attempt to set property '%s' on device '%s' " + "(type '%s') after it was realized", name, dev->id, + object_get_typename(OBJECT(dev))); + } else { + error_setg(errp, "Attempt to set property '%s' on anonymous device " + "(type '%s') after it was realized", name, + object_get_typename(OBJECT(dev))); + } +} + void *qdev_get_prop_ptr(DeviceState *dev, Property *prop) { void *ptr = dev; @@ -33,7 +47,7 @@ static void set_enum(Object *obj, Visitor *v, void *opaque, int *ptr = qdev_get_prop_ptr(dev, prop); if (dev->realized) { - error_set(errp, QERR_PERMISSION_DENIED); + qdev_prop_set_after_realize(dev, name, errp); return; } @@ -86,7 +100,7 @@ static void set_bit(Object *obj, Visitor *v, void *opaque, bool value; if (dev->realized) { - error_set(errp, QERR_PERMISSION_DENIED); + qdev_prop_set_after_realize(dev, name, errp); return; } @@ -126,7 +140,7 @@ static void set_uint8(Object *obj, Visitor *v, void *opaque, uint8_t *ptr = qdev_get_prop_ptr(dev, prop); if (dev->realized) { - error_set(errp, QERR_PERMISSION_DENIED); + qdev_prop_set_after_realize(dev, name, errp); return; } @@ -193,7 +207,7 @@ static void set_uint16(Object *obj, Visitor *v, void *opaque, uint16_t *ptr = qdev_get_prop_ptr(dev, prop); if (dev->realized) { - error_set(errp, QERR_PERMISSION_DENIED); + qdev_prop_set_after_realize(dev, name, errp); return; } @@ -226,7 +240,7 @@ static void set_uint32(Object *obj, Visitor *v, void *opaque, uint32_t *ptr = qdev_get_prop_ptr(dev, prop); if (dev->realized) { - error_set(errp, QERR_PERMISSION_DENIED); + qdev_prop_set_after_realize(dev, name, errp); return; } @@ -251,7 +265,7 @@ static void set_int32(Object *obj, Visitor *v, void *opaque, int32_t *ptr = qdev_get_prop_ptr(dev, prop); if (dev->realized) { - error_set(errp, QERR_PERMISSION_DENIED); + qdev_prop_set_after_realize(dev, name, errp); return; } @@ -324,7 +338,7 @@ static void set_uint64(Object *obj, Visitor *v, void *opaque, uint64_t *ptr = qdev_get_prop_ptr(dev, prop); if (dev->realized) { - error_set(errp, QERR_PERMISSION_DENIED); + qdev_prop_set_after_realize(dev, name, errp); return; } @@ -414,7 +428,7 @@ static void set_string(Object *obj, Visitor *v, void *opaque, char *str; if (dev->realized) { - error_set(errp, QERR_PERMISSION_DENIED); + qdev_prop_set_after_realize(dev, name, errp); return; } @@ -478,7 +492,7 @@ static void set_mac(Object *obj, Visitor *v, void *opaque, char *str, *p; if (dev->realized) { - error_set(errp, QERR_PERMISSION_DENIED); + qdev_prop_set_after_realize(dev, name, errp); return; } @@ -570,7 +584,7 @@ static void set_pci_devfn(Object *obj, Visitor *v, void *opaque, char *str; if (dev->realized) { - error_set(errp, QERR_PERMISSION_DENIED); + qdev_prop_set_after_realize(dev, name, errp); return; } @@ -641,7 +655,7 @@ static void set_blocksize(Object *obj, Visitor *v, void *opaque, const int64_t max = 32768; if (dev->realized) { - error_set(errp, QERR_PERMISSION_DENIED); + qdev_prop_set_after_realize(dev, name, errp); return; } @@ -709,7 +723,7 @@ static void set_pci_host_devaddr(Object *obj, Visitor *v, void *opaque, unsigned int slot = 0, func = 0; if (dev->realized) { - error_set(errp, QERR_PERMISSION_DENIED); + qdev_prop_set_after_realize(dev, name, errp); return; } @@ -779,6 +793,110 @@ PropertyInfo qdev_prop_pci_host_devaddr = { .set = set_pci_host_devaddr, }; +/* --- support for array properties --- */ + +/* Used as an opaque for the object properties we add for each + * array element. Note that the struct Property must be first + * in the struct so that a pointer to this works as the opaque + * for the underlying element's property hooks as well as for + * our own release callback. + */ +typedef struct { + struct Property prop; + char *propname; + ObjectPropertyRelease *release; +} ArrayElementProperty; + +/* object property release callback for array element properties: + * we call the underlying element's property release hook, and + * then free the memory we allocated when we added the property. + */ +static void array_element_release(Object *obj, const char *name, void *opaque) +{ + ArrayElementProperty *p = opaque; + if (p->release) { + p->release(obj, name, opaque); + } + g_free(p->propname); + g_free(p); +} + +static void set_prop_arraylen(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + /* Setter for the property which defines the length of a + * variable-sized property array. As well as actually setting the + * array-length field in the device struct, we have to create the + * array itself and dynamically add the corresponding properties. + */ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + uint32_t *alenptr = qdev_get_prop_ptr(dev, prop); + void **arrayptr = (void *)dev + prop->arrayoffset; + void *eltptr; + const char *arrayname; + int i; + + if (dev->realized) { + qdev_prop_set_after_realize(dev, name, errp); + return; + } + if (*alenptr) { + error_setg(errp, "array size property %s may not be set more than once", + name); + return; + } + visit_type_uint32(v, alenptr, name, errp); + if (error_is_set(errp)) { + return; + } + if (!*alenptr) { + return; + } + + /* DEFINE_PROP_ARRAY guarantees that name should start with this prefix; + * strip it off so we can get the name of the array itself. + */ + assert(strncmp(name, PROP_ARRAY_LEN_PREFIX, + strlen(PROP_ARRAY_LEN_PREFIX)) == 0); + arrayname = name + strlen(PROP_ARRAY_LEN_PREFIX); + + /* Note that it is the responsibility of the individual device's deinit + * to free the array proper. + */ + *arrayptr = eltptr = g_malloc0(*alenptr * prop->arrayfieldsize); + for (i = 0; i < *alenptr; i++, eltptr += prop->arrayfieldsize) { + char *propname = g_strdup_printf("%s[%d]", arrayname, i); + ArrayElementProperty *arrayprop = g_new0(ArrayElementProperty, 1); + arrayprop->release = prop->arrayinfo->release; + arrayprop->propname = propname; + arrayprop->prop.info = prop->arrayinfo; + arrayprop->prop.name = propname; + /* This ugly piece of pointer arithmetic sets up the offset so + * that when the underlying get/set hooks call qdev_get_prop_ptr + * they get the right answer despite the array element not actually + * being inside the device struct. + */ + arrayprop->prop.offset = eltptr - (void *)dev; + assert(qdev_get_prop_ptr(dev, &arrayprop->prop) == eltptr); + object_property_add(obj, propname, + arrayprop->prop.info->name, + arrayprop->prop.info->get, + arrayprop->prop.info->set, + array_element_release, + arrayprop, errp); + if (error_is_set(errp)) { + return; + } + } +} + +PropertyInfo qdev_prop_arraylen = { + .name = "uint32", + .get = get_uint32, + .set = set_prop_arraylen, +}; + /* --- public helpers --- */ static Property *qdev_prop_walk(Property *props, const char *name) diff --git a/hw/qdev-properties.h b/hw/qdev-properties.h index 0b0465c9b3..a37933998a 100644 --- a/hw/qdev-properties.h +++ b/hw/qdev-properties.h @@ -26,6 +26,7 @@ extern PropertyInfo qdev_prop_vlan; extern PropertyInfo qdev_prop_pci_devfn; extern PropertyInfo qdev_prop_blocksize; extern PropertyInfo qdev_prop_pci_host_devaddr; +extern PropertyInfo qdev_prop_arraylen; #define DEFINE_PROP(_name, _state, _field, _prop, _type) { \ .name = (_name), \ @@ -51,6 +52,44 @@ extern PropertyInfo qdev_prop_pci_host_devaddr; .defval = (bool)_defval, \ } +#define PROP_ARRAY_LEN_PREFIX "len-" + +/** + * DEFINE_PROP_ARRAY: + * @_name: name of the array + * @_state: name of the device state structure type + * @_field: uint32_t field in @_state to hold the array length + * @_arrayfield: field in @_state (of type '@_arraytype *') which + * will point to the array + * @_arrayprop: PropertyInfo defining what property the array elements have + * @_arraytype: C type of the array elements + * + * Define device properties for a variable-length array _name. A + * static property "len-arrayname" is defined. When the device creator + * sets this property to the desired length of array, further dynamic + * properties "arrayname[0]", "arrayname[1]", ... are defined so the + * device creator can set the array element values. Setting the + * "len-arrayname" property more than once is an error. + * + * When the array length is set, the @_field member of the device + * struct is set to the array length, and @_arrayfield is set to point + * to (zero-initialised) memory allocated for the array. For a zero + * length array, @_field will be set to 0 and @_arrayfield to NULL. + * It is the responsibility of the device deinit code to free the + * @_arrayfield memory. + */ +#define DEFINE_PROP_ARRAY(_name, _state, _field, \ + _arrayfield, _arrayprop, _arraytype) { \ + .name = (PROP_ARRAY_LEN_PREFIX _name), \ + .info = &(qdev_prop_arraylen), \ + .offset = offsetof(_state, _field) \ + + type_check(uint32_t, typeof_field(_state, _field)), \ + .qtype = QTYPE_QINT, \ + .arrayinfo = &(_arrayprop), \ + .arrayfieldsize = sizeof(_arraytype), \ + .arrayoffset = offsetof(_state, _arrayfield), \ + } + #define DEFINE_PROP_UINT8(_n, _s, _f, _d) \ DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_uint8, uint8_t) #define DEFINE_PROP_UINT16(_n, _s, _f, _d) \ @@ -128,4 +167,16 @@ void error_set_from_qdev_prop_error(Error **errp, int ret, DeviceState *dev, */ void qdev_property_add_static(DeviceState *dev, Property *prop, Error **errp); +/** + * @qdev_prop_set_after_realize: + * @dev: device + * @name: name of property + * @errp: indirect pointer to Error to be set + * Set the Error object to report that an attempt was made to set a property + * on a device after it has already been realized. This is a utility function + * which allows property-setter functions to easily report the error in + * a friendly format identifying both the device and the property. + */ +void qdev_prop_set_after_realize(DeviceState *dev, const char *name, + Error **errp); #endif @@ -119,11 +119,10 @@ DeviceState *qdev_create(BusState *bus, const char *name) if (bus) { error_report("Unknown device '%s' for bus '%s'", name, object_get_typename(OBJECT(bus))); - abort(); } else { error_report("Unknown device '%s' for default sysbus", name); - abort(); } + abort(); } return dev; @@ -565,7 +564,7 @@ static void qdev_set_legacy_property(Object *obj, Visitor *v, void *opaque, int ret; if (dev->realized) { - error_set(errp, QERR_PERMISSION_DENIED); + qdev_prop_set_after_realize(dev, name, errp); return; } @@ -713,6 +712,7 @@ static void device_initfn(Object *obj) DeviceState *dev = DEVICE(obj); ObjectClass *class; Property *prop; + Error *err = NULL; if (qdev_hotplug) { dev->hotplugged = 1; @@ -728,15 +728,18 @@ static void device_initfn(Object *obj) class = object_get_class(OBJECT(dev)); do { for (prop = DEVICE_CLASS(class)->props; prop && prop->name; prop++) { - qdev_property_add_legacy(dev, prop, NULL); - qdev_property_add_static(dev, prop, NULL); + qdev_property_add_legacy(dev, prop, &err); + assert_no_error(err); + qdev_property_add_static(dev, prop, &err); + assert_no_error(err); } class = object_class_get_parent(class); } while (class != object_class_by_name(TYPE_DEVICE)); qdev_prop_set_globals(dev); object_property_add_link(OBJECT(dev), "parent_bus", TYPE_BUS, - (Object **)&dev->parent_bus, NULL); + (Object **)&dev->parent_bus, &err); + assert_no_error(err); } /* Unlink device from bus and free the structure. */ diff --git a/hw/qxl-render.c b/hw/qxl-render.c index d77df42b7e..8cd9be434d 100644 --- a/hw/qxl-render.c +++ b/hw/qxl-render.c @@ -23,11 +23,12 @@ static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect) { + DisplaySurface *surface = qemu_console_surface(qxl->vga.con); + uint8_t *dst = surface_data(surface); uint8_t *src; - uint8_t *dst = ds_get_data(qxl->vga.ds); int len, i; - if (is_buffer_shared(qxl->vga.ds->surface)) { + if (is_buffer_shared(surface)) { return; } if (!qxl->guest_primary.data) { @@ -98,6 +99,7 @@ static void qxl_set_rect_to_surface(PCIQXLDevice *qxl, QXLRect *area) static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) { VGACommonState *vga = &qxl->vga; + DisplaySurface *surface; int i; if (qxl->guest_primary.resized) { @@ -112,8 +114,7 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) qxl->guest_primary.bytes_pp, qxl->guest_primary.bits_pp); if (qxl->guest_primary.qxl_stride > 0) { - qemu_free_displaysurface(vga->ds); - vga->ds->surface = qemu_create_displaysurface_from + surface = qemu_create_displaysurface_from (qxl->guest_primary.surface.width, qxl->guest_primary.surface.height, qxl->guest_primary.bits_pp, @@ -121,18 +122,18 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) qxl->guest_primary.data, false); } else { - qemu_resize_displaysurface(vga->ds, - qxl->guest_primary.surface.width, - qxl->guest_primary.surface.height); + surface = qemu_create_displaysurface + (qxl->guest_primary.surface.width, + qxl->guest_primary.surface.height); } - dpy_gfx_resize(vga->ds); + dpy_gfx_replace_surface(vga->con, surface); } for (i = 0; i < qxl->num_dirty_rects; i++) { if (qemu_spice_rect_is_empty(qxl->dirty+i)) { break; } qxl_blit(qxl, qxl->dirty+i); - dpy_gfx_update(vga->ds, + dpy_gfx_update(vga->con, qxl->dirty[i].left, qxl->dirty[i].top, qxl->dirty[i].right - qxl->dirty[i].left, qxl->dirty[i].bottom - qxl->dirty[i].top); @@ -236,7 +237,7 @@ int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext) return 1; } - if (!dpy_cursor_define_supported(qxl->ssd.ds)) { + if (!dpy_cursor_define_supported(qxl->vga.con)) { return 0; } @@ -118,8 +118,6 @@ static QXLMode qxl_modes[] = { QXL_MODE_EX(3200, 2400), }; -static PCIQXLDevice *qxl0; - static void qxl_send_events(PCIQXLDevice *d, uint32_t events); static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async); static void qxl_reset_memslots(PCIQXLDevice *d); @@ -1075,8 +1073,8 @@ static void qxl_enter_vga_mode(PCIQXLDevice *d) trace_qxl_enter_vga_mode(d->id); qemu_spice_create_host_primary(&d->ssd); d->mode = QXL_MODE_VGA; - dpy_gfx_resize(d->ssd.ds); vga_dirty_log_start(&d->vga); + vga_hw_update(); } static void qxl_exit_vga_mode(PCIQXLDevice *d) @@ -1784,7 +1782,7 @@ static void qxl_hw_screen_dump(void *opaque, const char *filename, bool cswitch, case QXL_MODE_COMPAT: case QXL_MODE_NATIVE: qxl_render_update(qxl); - ppm_save(filename, qxl->ssd.ds->surface, errp); + ppm_save(filename, qxl->ssd.ds, errp); break; case QXL_MODE_VGA: vga->screen_dump(vga, filename, cswitch, errp); @@ -1866,35 +1864,45 @@ static void qxl_vm_change_state_handler(void *opaque, int running, /* display change listener */ -static void display_update(struct DisplayState *ds, int x, int y, int w, int h) +static void display_update(DisplayChangeListener *dcl, + int x, int y, int w, int h) { - if (qxl0->mode == QXL_MODE_VGA) { - qemu_spice_display_update(&qxl0->ssd, x, y, w, h); + PCIQXLDevice *qxl = container_of(dcl, PCIQXLDevice, ssd.dcl); + + if (qxl->mode == QXL_MODE_VGA) { + qemu_spice_display_update(&qxl->ssd, x, y, w, h); } } -static void display_resize(struct DisplayState *ds) +static void display_switch(DisplayChangeListener *dcl, + struct DisplaySurface *surface) { - if (qxl0->mode == QXL_MODE_VGA) { - qemu_spice_display_resize(&qxl0->ssd); + PCIQXLDevice *qxl = container_of(dcl, PCIQXLDevice, ssd.dcl); + + qxl->ssd.ds = surface; + if (qxl->mode == QXL_MODE_VGA) { + qemu_spice_display_switch(&qxl->ssd, surface); } } -static void display_refresh(struct DisplayState *ds) +static void display_refresh(DisplayChangeListener *dcl) { - if (qxl0->mode == QXL_MODE_VGA) { - qemu_spice_display_refresh(&qxl0->ssd); + PCIQXLDevice *qxl = container_of(dcl, PCIQXLDevice, ssd.dcl); + + if (qxl->mode == QXL_MODE_VGA) { + qemu_spice_display_refresh(&qxl->ssd); } else { - qemu_mutex_lock(&qxl0->ssd.lock); - qemu_spice_cursor_refresh_unlocked(&qxl0->ssd); - qemu_mutex_unlock(&qxl0->ssd.lock); + qemu_mutex_lock(&qxl->ssd.lock); + qemu_spice_cursor_refresh_unlocked(&qxl->ssd); + qemu_mutex_unlock(&qxl->ssd.lock); } } -static DisplayChangeListener display_listener = { +static DisplayChangeListenerOps display_listener_ops = { + .dpy_name = "spice/qxl", .dpy_gfx_update = display_update, - .dpy_gfx_resize = display_resize, - .dpy_refresh = display_refresh, + .dpy_gfx_switch = display_switch, + .dpy_refresh = display_refresh, }; static void qxl_init_ramsize(PCIQXLDevice *qxl) @@ -2055,6 +2063,7 @@ static int qxl_init_primary(PCIDevice *dev) PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev); VGACommonState *vga = &qxl->vga; PortioList *qxl_vga_port_list = g_new(PortioList, 1); + DisplayState *ds; int rc; qxl->id = 0; @@ -2065,18 +2074,20 @@ static int qxl_init_primary(PCIDevice *dev) portio_list_init(qxl_vga_port_list, qxl_vga_portio_list, vga, "vga"); portio_list_add(qxl_vga_port_list, pci_address_space_io(dev), 0x3b0); - vga->ds = graphic_console_init(qxl_hw_update, qxl_hw_invalidate, - qxl_hw_screen_dump, qxl_hw_text_update, qxl); - qemu_spice_display_init_common(&qxl->ssd, vga->ds); - - qxl0 = qxl; + vga->con = graphic_console_init(qxl_hw_update, qxl_hw_invalidate, + qxl_hw_screen_dump, qxl_hw_text_update, + qxl); + qxl->ssd.con = vga->con, + qemu_spice_display_init_common(&qxl->ssd); rc = qxl_init_common(qxl); if (rc != 0) { return rc; } - register_displaychangelistener(vga->ds, &display_listener); + qxl->ssd.dcl.ops = &display_listener_ops; + ds = qemu_console_displaystate(vga->con); + register_displaychangelistener(ds, &qxl->ssd.dcl); return rc; } diff --git a/hw/rtl8139.c b/hw/rtl8139.c index 786b875c58..9369507422 100644 --- a/hw/rtl8139.c +++ b/hw/rtl8139.c @@ -3530,7 +3530,7 @@ static void rtl8139_class_init(ObjectClass *klass, void *data) k->init = pci_rtl8139_init; k->exit = pci_rtl8139_uninit; - k->romfile = "pxe-rtl8139.rom"; + k->romfile = "efi-rtl8139.rom"; k->vendor_id = PCI_VENDOR_ID_REALTEK; k->device_id = PCI_DEVICE_ID_REALTEK_8139; k->revision = RTL8139_PCI_REVID; /* >=0x20 is for 8139C+ */ diff --git a/hw/s390x/s390-virtio-bus.c b/hw/s390x/s390-virtio-bus.c index d9b7f83878..8c529c14d0 100644 --- a/hw/s390x/s390-virtio-bus.c +++ b/hw/s390x/s390-virtio-bus.c @@ -162,16 +162,23 @@ static int s390_virtio_net_init(VirtIOS390Device *dev) return s390_virtio_device_init(dev, vdev); } -static int s390_virtio_blk_init(VirtIOS390Device *dev) +static int s390_virtio_blk_init(VirtIOS390Device *s390_dev) { - VirtIODevice *vdev; - - vdev = virtio_blk_init((DeviceState *)dev, &dev->blk); - if (!vdev) { + VirtIOBlkS390 *dev = VIRTIO_BLK_S390(s390_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + virtio_blk_set_conf(vdev, &(dev->blk)); + qdev_set_parent_bus(vdev, BUS(&s390_dev->bus)); + if (qdev_init(vdev) < 0) { return -1; } + return s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); +} - return s390_virtio_device_init(dev, vdev); +static void s390_virtio_blk_instance_init(Object *obj) +{ + VirtIOBlkS390 *dev = VIRTIO_BLK_S390(obj); + object_initialize(OBJECT(&dev->vdev), TYPE_VIRTIO_BLK); + object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL); } static int s390_virtio_serial_init(VirtIOS390Device *dev) @@ -195,16 +202,24 @@ static int s390_virtio_serial_init(VirtIOS390Device *dev) return r; } -static int s390_virtio_scsi_init(VirtIOS390Device *dev) +static int s390_virtio_scsi_init(VirtIOS390Device *s390_dev) { - VirtIODevice *vdev; + VirtIOSCSIS390 *dev = VIRTIO_SCSI_S390(s390_dev); + DeviceState *vdev = DEVICE(&dev->vdev); - vdev = virtio_scsi_init((DeviceState *)dev, &dev->scsi); - if (!vdev) { + qdev_set_parent_bus(vdev, BUS(&s390_dev->bus)); + if (qdev_init(vdev) < 0) { return -1; } - return s390_virtio_device_init(dev, vdev); + return s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); +} + +static void s390_virtio_scsi_instance_init(Object *obj) +{ + VirtIOSCSIS390 *dev = VIRTIO_SCSI_S390(obj); + object_initialize(OBJECT(&dev->vdev), TYPE_VIRTIO_SCSI); + object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL); } static int s390_virtio_rng_init(VirtIOS390Device *dev) @@ -428,12 +443,7 @@ static const TypeInfo s390_virtio_net = { }; static Property s390_virtio_blk_properties[] = { - DEFINE_BLOCK_PROPERTIES(VirtIOS390Device, blk.conf), - DEFINE_BLOCK_CHS_PROPERTIES(VirtIOS390Device, blk.conf), - DEFINE_PROP_STRING("serial", VirtIOS390Device, blk.serial), -#ifdef __linux__ - DEFINE_PROP_BIT("scsi", VirtIOS390Device, blk.scsi, 0, true), -#endif + DEFINE_VIRTIO_BLK_PROPERTIES(VirtIOBlkS390, blk), DEFINE_PROP_END_OF_LIST(), }; @@ -449,7 +459,8 @@ static void s390_virtio_blk_class_init(ObjectClass *klass, void *data) static const TypeInfo s390_virtio_blk = { .name = "virtio-blk-s390", .parent = TYPE_VIRTIO_S390_DEVICE, - .instance_size = sizeof(VirtIOS390Device), + .instance_size = sizeof(VirtIOBlkS390), + .instance_init = s390_virtio_blk_instance_init, .class_init = s390_virtio_blk_class_init, }; @@ -535,7 +546,8 @@ static const TypeInfo virtio_s390_device_info = { }; static Property s390_virtio_scsi_properties[] = { - DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOS390Device, host_features, scsi), + DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOSCSIS390, vdev.conf), + DEFINE_VIRTIO_SCSI_FEATURES(VirtIOS390Device, host_features), DEFINE_PROP_END_OF_LIST(), }; @@ -549,9 +561,10 @@ static void s390_virtio_scsi_class_init(ObjectClass *klass, void *data) } static const TypeInfo s390_virtio_scsi = { - .name = "virtio-scsi-s390", + .name = TYPE_VIRTIO_SCSI_S390, .parent = TYPE_VIRTIO_S390_DEVICE, - .instance_size = sizeof(VirtIOS390Device), + .instance_size = sizeof(VirtIOSCSIS390), + .instance_init = s390_virtio_scsi_instance_init, .class_init = s390_virtio_scsi_class_init, }; @@ -588,7 +601,7 @@ void virtio_s390_bus_new(VirtioBusState *bus, VirtIOS390Device *dev) BusState *qbus; qbus_create_inplace((BusState *)bus, TYPE_VIRTIO_S390_BUS, qdev, NULL); qbus = BUS(bus); - qbus->allow_hotplug = 0; + qbus->allow_hotplug = 1; } static void virtio_s390_bus_class_init(ObjectClass *klass, void *data) diff --git a/hw/s390x/s390-virtio-bus.h b/hw/s390x/s390-virtio-bus.h index 4aacf83998..ebe8794204 100644 --- a/hw/s390x/s390-virtio-bus.h +++ b/hw/s390x/s390-virtio-bus.h @@ -89,12 +89,10 @@ struct VirtIOS390Device { ram_addr_t feat_offs; uint8_t feat_len; VirtIODevice *vdev; - VirtIOBlkConf blk; NICConf nic; uint32_t host_features; virtio_serial_conf serial; virtio_net_conf net; - VirtIOSCSIConf scsi; VirtIORNGConf rng; VirtioBusState bus; }; @@ -120,5 +118,27 @@ VirtIOS390Device *s390_virtio_bus_find_mem(VirtIOS390Bus *bus, ram_addr_t mem); void s390_virtio_device_sync(VirtIOS390Device *dev); void s390_virtio_reset_idx(VirtIOS390Device *dev); +/* virtio-blk-s390 */ + +#define TYPE_VIRTIO_BLK_S390 "virtio-blk-s390" +#define VIRTIO_BLK_S390(obj) \ + OBJECT_CHECK(VirtIOBlkS390, (obj), TYPE_VIRTIO_BLK_S390) + +typedef struct VirtIOBlkS390 { + VirtIOS390Device parent_obj; + VirtIOBlock vdev; + VirtIOBlkConf blk; +} VirtIOBlkS390; + +/* virtio-scsi-s390 */ + +#define TYPE_VIRTIO_SCSI_S390 "virtio-scsi-s390" +#define VIRTIO_SCSI_S390(obj) \ + OBJECT_CHECK(VirtIOSCSIS390, (obj), TYPE_VIRTIO_SCSI_S390) + +typedef struct VirtIOSCSIS390 { + VirtIOS390Device parent_obj; + VirtIOSCSI vdev; +} VirtIOSCSIS390; #endif diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index d4364143ea..76b63e2ca6 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -31,6 +31,9 @@ static int virtio_ccw_hcall_notify(const uint64_t *args) if (!sch || !css_subch_visible(sch)) { return -EINVAL; } + if (queue >= VIRTIO_PCI_QUEUE_MAX) { + return -EINVAL; + } virtio_queue_notify(virtio_ccw_get_vdev(sch), queue); return 0; diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index d4361f6e3e..7e79c5795a 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -570,22 +570,24 @@ static int virtio_ccw_net_exit(VirtioCcwDevice *dev) return virtio_ccw_exit(dev); } -static int virtio_ccw_blk_init(VirtioCcwDevice *dev) +static int virtio_ccw_blk_init(VirtioCcwDevice *ccw_dev) { - VirtIODevice *vdev; - - vdev = virtio_blk_init((DeviceState *)dev, &dev->blk); - if (!vdev) { + VirtIOBlkCcw *dev = VIRTIO_BLK_CCW(ccw_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + virtio_blk_set_conf(vdev, &(dev->blk)); + qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); + if (qdev_init(vdev) < 0) { return -1; } - return virtio_ccw_device_init(dev, vdev); + return virtio_ccw_device_init(ccw_dev, VIRTIO_DEVICE(vdev)); } -static int virtio_ccw_blk_exit(VirtioCcwDevice *dev) +static void virtio_ccw_blk_instance_init(Object *obj) { - virtio_blk_exit(dev->vdev); - return virtio_ccw_exit(dev); + VirtIOBlkCcw *dev = VIRTIO_BLK_CCW(obj); + object_initialize(OBJECT(&dev->vdev), TYPE_VIRTIO_BLK); + object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL); } static int virtio_ccw_serial_init(VirtioCcwDevice *dev) @@ -624,22 +626,24 @@ static int virtio_ccw_balloon_exit(VirtioCcwDevice *dev) return virtio_ccw_exit(dev); } -static int virtio_ccw_scsi_init(VirtioCcwDevice *dev) +static int virtio_ccw_scsi_init(VirtioCcwDevice *ccw_dev) { - VirtIODevice *vdev; + VirtIOSCSICcw *dev = VIRTIO_SCSI_CCW(ccw_dev); + DeviceState *vdev = DEVICE(&dev->vdev); - vdev = virtio_scsi_init((DeviceState *)dev, &dev->scsi); - if (!vdev) { + qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); + if (qdev_init(vdev) < 0) { return -1; } - return virtio_ccw_device_init(dev, vdev); + return virtio_ccw_device_init(ccw_dev, VIRTIO_DEVICE(vdev)); } -static int virtio_ccw_scsi_exit(VirtioCcwDevice *dev) +static void virtio_ccw_scsi_instance_init(Object *obj) { - virtio_scsi_exit(dev->vdev); - return virtio_ccw_exit(dev); + VirtIOSCSICcw *dev = VIRTIO_SCSI_CCW(obj); + object_initialize(OBJECT(&dev->vdev), TYPE_VIRTIO_SCSI); + object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL); } static int virtio_ccw_rng_init(VirtioCcwDevice *dev) @@ -754,12 +758,8 @@ static const TypeInfo virtio_ccw_net = { static Property virtio_ccw_blk_properties[] = { DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), - DEFINE_BLOCK_PROPERTIES(VirtioCcwDevice, blk.conf), - DEFINE_PROP_STRING("serial", VirtioCcwDevice, blk.serial), -#ifdef __linux__ - DEFINE_PROP_BIT("scsi", VirtioCcwDevice, blk.scsi, 0, true), -#endif DEFINE_VIRTIO_BLK_FEATURES(VirtioCcwDevice, host_features[0]), + DEFINE_VIRTIO_BLK_PROPERTIES(VirtIOBlkCcw, blk), DEFINE_PROP_END_OF_LIST(), }; @@ -769,15 +769,16 @@ static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->init = virtio_ccw_blk_init; - k->exit = virtio_ccw_blk_exit; + k->exit = virtio_ccw_exit; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_blk_properties; } static const TypeInfo virtio_ccw_blk = { - .name = "virtio-blk-ccw", + .name = TYPE_VIRTIO_BLK_CCW, .parent = TYPE_VIRTIO_CCW_DEVICE, - .instance_size = sizeof(VirtioCcwDevice), + .instance_size = sizeof(VirtIOBlkCcw), + .instance_init = virtio_ccw_blk_instance_init, .class_init = virtio_ccw_blk_class_init, }; @@ -833,7 +834,8 @@ static const TypeInfo virtio_ccw_balloon = { static Property virtio_ccw_scsi_properties[] = { DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), - DEFINE_VIRTIO_SCSI_PROPERTIES(VirtioCcwDevice, host_features[0], scsi), + DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOSCSICcw, vdev.conf), + DEFINE_VIRTIO_SCSI_FEATURES(VirtioCcwDevice, host_features[0]), DEFINE_PROP_END_OF_LIST(), }; @@ -843,15 +845,16 @@ static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->init = virtio_ccw_scsi_init; - k->exit = virtio_ccw_scsi_exit; + k->exit = virtio_ccw_exit; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_scsi_properties; } static const TypeInfo virtio_ccw_scsi = { - .name = "virtio-scsi-ccw", + .name = TYPE_VIRTIO_SCSI_CCW, .parent = TYPE_VIRTIO_CCW_DEVICE, - .instance_size = sizeof(VirtioCcwDevice), + .instance_size = sizeof(VirtIOSCSICcw), + .instance_init = virtio_ccw_scsi_instance_init, .class_init = virtio_ccw_scsi_class_init, }; @@ -982,7 +985,7 @@ void virtio_ccw_bus_new(VirtioBusState *bus, VirtioCcwDevice *dev) qbus_create_inplace((BusState *)bus, TYPE_VIRTIO_CCW_BUS, qdev, NULL); qbus = BUS(bus); - qbus->allow_hotplug = 0; + qbus->allow_hotplug = 1; } static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data) diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index 88c46c081b..d9f73997df 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -72,12 +72,10 @@ struct VirtioCcwDevice { SubchDev *sch; VirtIODevice *vdev; char *bus_id; - VirtIOBlkConf blk; NICConf nic; uint32_t host_features[VIRTIO_CCW_FEATURE_SIZE]; virtio_serial_conf serial; virtio_net_conf net; - VirtIOSCSIConf scsi; VirtIORNGConf rng; VirtioBusState bus; /* Guest provided values: */ @@ -94,6 +92,30 @@ typedef struct VirtualCssBus { #define VIRTUAL_CSS_BUS(obj) \ OBJECT_CHECK(VirtualCssBus, (obj), TYPE_VIRTUAL_CSS_BUS) +/* virtio-scsi-ccw */ + +#define TYPE_VIRTIO_SCSI_CCW "virtio-scsi-ccw" +#define VIRTIO_SCSI_CCW(obj) \ + OBJECT_CHECK(VirtIOSCSICcw, (obj), TYPE_VIRTIO_SCSI_CCW) + +typedef struct VirtIOSCSICcw { + VirtioCcwDevice parent_obj; + VirtIOSCSI vdev; +} VirtIOSCSICcw; + +/* virtio-blk-ccw */ + +#define TYPE_VIRTIO_BLK_CCW "virtio-blk-ccw" +#define VIRTIO_BLK_CCW(obj) \ + OBJECT_CHECK(VirtIOBlkCcw, (obj), TYPE_VIRTIO_BLK_CCW) + +typedef struct VirtIOBlkCcw { + VirtioCcwDevice parent_obj; + VirtIOBlock vdev; + VirtIOBlkConf blk; +} VirtIOBlkCcw; + + VirtualCssBus *virtual_css_bus_init(void); void virtio_ccw_device_update_status(SubchDev *sch); VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch); diff --git a/hw/sdhci.c b/hw/sdhci.c index 93feada049..4a29e6cf7f 100644 --- a/hw/sdhci.c +++ b/hw/sdhci.c @@ -763,7 +763,7 @@ static void sdhci_do_adma(SDHCIState *s) } } - /* we have unfinished bussiness - reschedule to continue ADMA */ + /* we have unfinished business - reschedule to continue ADMA */ qemu_mod_timer(s->transfer_timer, qemu_get_clock_ns(vm_clock) + SDHC_TRANSFER_DELAY); } diff --git a/hw/serial.c b/hw/serial.c index 48a5eb62b9..0ccc499285 100644 --- a/hw/serial.c +++ b/hw/serial.c @@ -306,7 +306,7 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val, SerialState *s = opaque; addr &= 7; - DPRINTF("write addr=0x%02x val=0x%02x\n", addr, val); + DPRINTF("write addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 "\n", addr, val); switch(addr) { default: case 0: @@ -527,7 +527,7 @@ static uint64_t serial_ioport_read(void *opaque, hwaddr addr, unsigned size) ret = s->scr; break; } - DPRINTF("read addr=0x%02x val=0x%02x\n", addr, ret); + DPRINTF("read addr=0x%" HWADDR_PRIx " val=0x%02x\n", addr, ret); return ret; } diff --git a/hw/sm501.c b/hw/sm501.c index 0e019111bb..93a06c90b9 100644 --- a/hw/sm501.c +++ b/hw/sm501.c @@ -454,7 +454,7 @@ static const uint32_t sm501_mem_local_size[] = { typedef struct SM501State { /* graphic console status */ - DisplayState *ds; + QemuConsole *con; /* status & internal resources */ hwaddr base; @@ -1234,9 +1234,9 @@ static draw_hwc_line_func * draw_hwc_line_funcs[] = { draw_hwc_line_16bgr, }; -static inline int get_depth_index(DisplayState *s) +static inline int get_depth_index(DisplaySurface *surface) { - switch(ds_get_bits_per_pixel(s)) { + switch (surface_bits_per_pixel(surface)) { default: case 8: return 0; @@ -1245,26 +1245,28 @@ static inline int get_depth_index(DisplayState *s) case 16: return 2; case 32: - if (is_surface_bgr(s->surface)) - return 4; - else - return 3; + if (is_surface_bgr(surface)) { + return 4; + } else { + return 3; + } } } static void sm501_draw_crt(SM501State * s) { + DisplaySurface *surface = qemu_console_surface(s->con); int y; int width = (s->dc_crt_h_total & 0x00000FFF) + 1; int height = (s->dc_crt_v_total & 0x00000FFF) + 1; uint8_t * src = s->local_mem; int src_bpp = 0; - int dst_bpp = ds_get_bytes_per_pixel(s->ds) + (ds_get_bits_per_pixel(s->ds) % 8 ? 1 : 0); + int dst_bpp = surface_bytes_per_pixel(surface); uint32_t * palette = (uint32_t *)&s->dc_palette[SM501_DC_CRT_PALETTE - SM501_DC_PANEL_PALETTE]; uint8_t hwc_palette[3 * 3]; - int ds_depth_index = get_depth_index(s->ds); + int ds_depth_index = get_depth_index(surface); draw_line_func * draw_line = NULL; draw_hwc_line_func * draw_hwc_line = NULL; int full_update = 0; @@ -1312,7 +1314,8 @@ static void sm501_draw_crt(SM501State * s) /* adjust console size */ if (s->last_width != width || s->last_height != height) { - qemu_console_resize(s->ds, width, height); + qemu_console_resize(s->con, width, height); + surface = qemu_console_surface(s->con); s->last_width = width; s->last_height = height; full_update = 1; @@ -1331,7 +1334,8 @@ static void sm501_draw_crt(SM501State * s) /* draw line and change status */ if (update) { - uint8_t * d = &(ds_get_data(s->ds)[y * width * dst_bpp]); + uint8_t *d = surface_data(surface); + d += y * width * dst_bpp; /* draw graphics layer */ draw_line(d, src, width, palette); @@ -1350,7 +1354,7 @@ static void sm501_draw_crt(SM501State * s) } else { if (y_start >= 0) { /* flush to display */ - dpy_gfx_update(s->ds, 0, y_start, width, y - y_start); + dpy_gfx_update(s->con, 0, y_start, width, y - y_start); y_start = -1; } } @@ -1361,7 +1365,7 @@ static void sm501_draw_crt(SM501State * s) /* complete flush to display */ if (y_start >= 0) - dpy_gfx_update(s->ds, 0, y_start, width, y - y_start); + dpy_gfx_update(s->con, 0, y_start, width, y - y_start); /* clear dirty flags */ if (page_min != ~0l) { @@ -1441,6 +1445,6 @@ void sm501_init(MemoryRegion *address_space_mem, uint32_t base, } /* create qemu graphic console */ - s->ds = graphic_console_init(sm501_update_display, NULL, - NULL, NULL, s); + s->con = graphic_console_init(sm501_update_display, NULL, + NULL, NULL, s); } diff --git a/hw/spapr_pci.c b/hw/spapr_pci.c index 20b9015502..3e0d8d12fb 100644 --- a/hw/spapr_pci.c +++ b/hw/spapr_pci.c @@ -518,6 +518,7 @@ static int spapr_phb_init(SysBusDevice *s) { sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(s); PCIHostState *phb = PCI_HOST_BRIDGE(s); + const char *busname; char *namebuf; int i; PCIBus *bus; @@ -575,9 +576,6 @@ static int spapr_phb_init(SysBusDevice *s) } sphb->dtbusname = g_strdup_printf("pci@%" PRIx64, sphb->buid); - if (!sphb->busname) { - sphb->busname = sphb->dtbusname; - } namebuf = alloca(strlen(sphb->dtbusname) + 32); @@ -621,7 +619,26 @@ static int spapr_phb_init(SysBusDevice *s) &sphb->msiwindow); } - bus = pci_register_bus(DEVICE(s), sphb->busname, + /* + * Selecting a busname is more complex than you'd think, due to + * interacting constraints. If the user has specified an id + * explicitly for the phb , then we want to use the qdev default + * of naming the bus based on the bridge device (so the user can + * then assign devices to it in the way they expect). For the + * first / default PCI bus (index=0) we want to use just "pci" + * because libvirt expects there to be a bus called, simply, + * "pci". Otherwise, we use the same name as in the device tree, + * since it's unique by construction, and makes the guest visible + * BUID clear. + */ + if (s->qdev.id) { + busname = NULL; + } else if (sphb->index == 0) { + busname = "pci"; + } else { + busname = sphb->dtbusname; + } + bus = pci_register_bus(DEVICE(s), busname, pci_spapr_set_irq, pci_spapr_map_irq, sphb, &sphb->memspace, &sphb->iospace, PCI_DEVFN(0, 0), PCI_NUM_PINS, TYPE_PCI_BUS); @@ -663,7 +680,6 @@ static void spapr_phb_reset(DeviceState *qdev) } static Property spapr_phb_properties[] = { - DEFINE_PROP_STRING("busname", sPAPRPHBState, busname), DEFINE_PROP_INT32("index", sPAPRPHBState, index, -1), DEFINE_PROP_HEX64("buid", sPAPRPHBState, buid, -1), DEFINE_PROP_HEX32("liobn", sPAPRPHBState, dma_liobn, -1), @@ -694,14 +710,12 @@ static const TypeInfo spapr_phb_info = { .class_init = spapr_phb_class_init, }; -PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index, - const char *busname) +PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index) { DeviceState *dev; dev = qdev_create(NULL, TYPE_SPAPR_PCI_HOST_BRIDGE); qdev_prop_set_uint32(dev, "index", index); - qdev_prop_set_string(dev, "busname", busname); qdev_init_nofail(dev); return PCI_HOST_BRIDGE(dev); diff --git a/hw/spapr_pci.h b/hw/spapr_pci.h index 8bb3c62c3d..8bd8a663c5 100644 --- a/hw/spapr_pci.h +++ b/hw/spapr_pci.h @@ -39,7 +39,6 @@ typedef struct sPAPRPHBState { int32_t index; uint64_t buid; - char *busname; char *dtbusname; MemoryRegion memspace, iospace; @@ -82,8 +81,7 @@ static inline qemu_irq spapr_phb_lsi_qirq(struct sPAPRPHBState *phb, int pin) return xics_get_qirq(spapr->icp, phb->lsi_table[pin].irq); } -PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index, - const char *busname); +PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index); int spapr_populate_pci_dt(sPAPRPHBState *phb, uint32_t xics_phandle, diff --git a/hw/ssd0303.c b/hw/ssd0303.c index db50909734..68d1f24b06 100644 --- a/hw/ssd0303.c +++ b/hw/ssd0303.c @@ -43,7 +43,7 @@ enum ssd0303_cmd { typedef struct { I2CSlave i2c; - DisplayState *ds; + QemuConsole *con; int row; int col; int start_line; @@ -191,6 +191,7 @@ static void ssd0303_event(I2CSlave *i2c, enum i2c_event event) static void ssd0303_update_display(void *opaque) { ssd0303_state *s = (ssd0303_state *)opaque; + DisplaySurface *surface = qemu_console_surface(s->con); uint8_t *dest; uint8_t *src; int x; @@ -204,7 +205,7 @@ static void ssd0303_update_display(void *opaque) if (!s->redraw) return; - switch (ds_get_bits_per_pixel(s->ds)) { + switch (surface_bits_per_pixel(surface)) { case 0: return; case 15: @@ -236,7 +237,7 @@ static void ssd0303_update_display(void *opaque) colors[0] = colortab + dest_width; colors[1] = colortab; } - dest = ds_get_data(s->ds); + dest = surface_data(surface); for (y = 0; y < 16; y++) { line = (y + s->start_line) & 63; src = s->framebuffer + 132 * (line >> 3) + 36; @@ -252,7 +253,7 @@ static void ssd0303_update_display(void *opaque) } } s->redraw = 0; - dpy_gfx_update(s->ds, 0, 0, 96 * MAGNIFY, 16 * MAGNIFY); + dpy_gfx_update(s->con, 0, 0, 96 * MAGNIFY, 16 * MAGNIFY); } static void ssd0303_invalidate_display(void * opaque) @@ -287,10 +288,10 @@ static int ssd0303_init(I2CSlave *i2c) { ssd0303_state *s = FROM_I2C_SLAVE(ssd0303_state, i2c); - s->ds = graphic_console_init(ssd0303_update_display, - ssd0303_invalidate_display, - NULL, NULL, s); - qemu_console_resize(s->ds, 96 * MAGNIFY, 16 * MAGNIFY); + s->con = graphic_console_init(ssd0303_update_display, + ssd0303_invalidate_display, + NULL, NULL, s); + qemu_console_resize(s->con, 96 * MAGNIFY, 16 * MAGNIFY); return 0; } diff --git a/hw/ssd0323.c b/hw/ssd0323.c index 27b4151994..5cf2f7058f 100644 --- a/hw/ssd0323.c +++ b/hw/ssd0323.c @@ -45,7 +45,7 @@ enum ssd0323_mode typedef struct { SSISlave ssidev; - DisplayState *ds; + QemuConsole *con; int cmd_len; int cmd; @@ -175,6 +175,7 @@ static uint32_t ssd0323_transfer(SSISlave *dev, uint32_t data) static void ssd0323_update_display(void *opaque) { ssd0323_state *s = (ssd0323_state *)opaque; + DisplaySurface *surface = qemu_console_surface(s->con); uint8_t *dest; uint8_t *src; int x; @@ -189,7 +190,7 @@ static void ssd0323_update_display(void *opaque) if (!s->redraw) return; - switch (ds_get_bits_per_pixel(s->ds)) { + switch (surface_bits_per_pixel(surface)) { case 0: return; case 15: @@ -212,7 +213,7 @@ static void ssd0323_update_display(void *opaque) for (i = 0; i < 16; i++) { int n; colors[i] = p; - switch (ds_get_bits_per_pixel(s->ds)) { + switch (surface_bits_per_pixel(surface)) { case 15: n = i * 2 + (i >> 3); p[0] = n | (n << 5); @@ -235,7 +236,7 @@ static void ssd0323_update_display(void *opaque) p += dest_width; } /* TODO: Implement row/column remapping. */ - dest = ds_get_data(s->ds); + dest = surface_data(surface); for (y = 0; y < 64; y++) { line = y; src = s->framebuffer + 64 * line; @@ -260,7 +261,7 @@ static void ssd0323_update_display(void *opaque) } } s->redraw = 0; - dpy_gfx_update(s->ds, 0, 0, 128 * MAGNIFY, 64 * MAGNIFY); + dpy_gfx_update(s->con, 0, 0, 128 * MAGNIFY, 64 * MAGNIFY); } static void ssd0323_invalidate_display(void * opaque) @@ -336,10 +337,10 @@ static int ssd0323_init(SSISlave *dev) s->col_end = 63; s->row_end = 79; - s->ds = graphic_console_init(ssd0323_update_display, - ssd0323_invalidate_display, - NULL, NULL, s); - qemu_console_resize(s->ds, 128 * MAGNIFY, 64 * MAGNIFY); + s->con = graphic_console_init(ssd0323_update_display, + ssd0323_invalidate_display, + NULL, NULL, s); + qemu_console_resize(s->con, 128 * MAGNIFY, 64 * MAGNIFY); qdev_init_gpio_in(&dev->qdev, ssd0323_cd, 1); diff --git a/hw/tc6393xb.c b/hw/tc6393xb.c index 0755463a1d..79c971b439 100644 --- a/hw/tc6393xb.c +++ b/hw/tc6393xb.c @@ -125,7 +125,7 @@ struct TC6393xbState { DeviceState *flash; ECCState ecc; - DisplayState *ds; + QemuConsole *con; MemoryRegion vram; uint16_t *vram_ptr; uint32_t scr_width, scr_height; /* in pixels */ @@ -433,7 +433,9 @@ static void tc6393xb_nand_writeb(TC6393xbState *s, hwaddr addr, uint32_t value) static void tc6393xb_draw_graphic(TC6393xbState *s, int full_update) { - switch (ds_get_bits_per_pixel(s->ds)) { + DisplaySurface *surface = qemu_console_surface(s->con); + + switch (surface_bits_per_pixel(surface)) { case 8: tc6393xb_draw_graphic8(s); break; @@ -450,34 +452,37 @@ static void tc6393xb_draw_graphic(TC6393xbState *s, int full_update) tc6393xb_draw_graphic32(s); break; default: - printf("tc6393xb: unknown depth %d\n", ds_get_bits_per_pixel(s->ds)); + printf("tc6393xb: unknown depth %d\n", + surface_bits_per_pixel(surface)); return; } - dpy_gfx_update(s->ds, 0, 0, s->scr_width, s->scr_height); + dpy_gfx_update(s->con, 0, 0, s->scr_width, s->scr_height); } static void tc6393xb_draw_blank(TC6393xbState *s, int full_update) { + DisplaySurface *surface = qemu_console_surface(s->con); int i, w; uint8_t *d; if (!full_update) return; - w = s->scr_width * ((ds_get_bits_per_pixel(s->ds) + 7) >> 3); - d = ds_get_data(s->ds); + w = s->scr_width * surface_bytes_per_pixel(surface); + d = surface_data(surface); for(i = 0; i < s->scr_height; i++) { memset(d, 0, w); - d += ds_get_linesize(s->ds); + d += surface_stride(surface); } - dpy_gfx_update(s->ds, 0, 0, s->scr_width, s->scr_height); + dpy_gfx_update(s->con, 0, 0, s->scr_width, s->scr_height); } static void tc6393xb_update_display(void *opaque) { TC6393xbState *s = opaque; + DisplaySurface *surface = qemu_console_surface(s->con); int full_update; if (s->scr_width == 0 || s->scr_height == 0) @@ -488,8 +493,9 @@ static void tc6393xb_update_display(void *opaque) s->blanked = s->blank; full_update = 1; } - if (s->scr_width != ds_get_width(s->ds) || s->scr_height != ds_get_height(s->ds)) { - qemu_console_resize(s->ds, s->scr_width, s->scr_height); + if (s->scr_width != surface_width(surface) || + s->scr_height != surface_height(surface)) { + qemu_console_resize(s->con, s->scr_width, s->scr_height); full_update = 1; } if (s->blanked) @@ -577,7 +583,7 @@ TC6393xbState *tc6393xb_init(MemoryRegion *sysmem, uint32_t base, qemu_irq irq) memory_region_add_subregion(sysmem, base + 0x100000, &s->vram); s->scr_width = 480; s->scr_height = 640; - s->ds = graphic_console_init(tc6393xb_update_display, + s->con = graphic_console_init(tc6393xb_update_display, NULL, /* invalidate */ NULL, /* screen_dump */ NULL, /* text_update */ diff --git a/hw/tc6393xb_template.h b/hw/tc6393xb_template.h index 4cbbad5dae..154aafd400 100644 --- a/hw/tc6393xb_template.h +++ b/hw/tc6393xb_template.h @@ -37,17 +37,18 @@ static void glue(tc6393xb_draw_graphic, BITS)(TC6393xbState *s) { + DisplaySurface *surface = qemu_console_surface(s->con); int i; uint16_t *data_buffer; uint8_t *data_display; data_buffer = s->vram_ptr; - data_display = ds_get_data(s->ds); + data_display = surface_data(surface); for(i = 0; i < s->scr_height; i++) { #if (BITS == 16) memcpy(data_display, data_buffer, s->scr_width * 2); data_buffer += s->scr_width; - data_display += ds_get_linesize(s->ds); + data_display += surface_stride(surface); #else int j; for (j = 0; j < s->scr_width; j++, data_display += BITS / 8, data_buffer++) { @@ -38,7 +38,7 @@ typedef struct TCXState { SysBusDevice busdev; hwaddr addr; - DisplayState *ds; + QemuConsole *con; uint8_t *vram; uint32_t *vram24, *cplane; MemoryRegion vram_mem; @@ -75,9 +75,11 @@ static void tcx24_set_dirty(TCXState *s) static void update_palette_entries(TCXState *s, int start, int end) { + DisplaySurface *surface = qemu_console_surface(s->con); int i; - for(i = start; i < end; i++) { - switch(ds_get_bits_per_pixel(s->ds)) { + + for (i = start; i < end; i++) { + switch (surface_bits_per_pixel(surface)) { default: case 8: s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]); @@ -89,10 +91,11 @@ static void update_palette_entries(TCXState *s, int start, int end) s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]); break; case 32: - if (is_surface_bgr(s->ds->surface)) + if (is_surface_bgr(surface)) { s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]); - else + } else { s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]); + } break; } } @@ -151,12 +154,13 @@ static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d, const uint32_t *cplane, const uint32_t *s24) { + DisplaySurface *surface = qemu_console_surface(s1->con); int x, bgr, r, g, b; uint8_t val, *p8; uint32_t *p = (uint32_t *)d; uint32_t dval; - bgr = is_surface_bgr(s1->ds->surface); + bgr = is_surface_bgr(surface); for(x = 0; x < width; x++, s++, s24++) { if ((be32_to_cpu(*cplane++) & 0xff000000) == 0x03000000) { // 24-bit direct, BGR order @@ -213,23 +217,26 @@ static inline void reset_dirty(TCXState *ts, ram_addr_t page_min, static void tcx_update_display(void *opaque) { TCXState *ts = opaque; + DisplaySurface *surface = qemu_console_surface(ts->con); ram_addr_t page, page_min, page_max; int y, y_start, dd, ds; uint8_t *d, *s; void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width); - if (ds_get_bits_per_pixel(ts->ds) == 0) + if (surface_bits_per_pixel(surface) == 0) { return; + } + page = 0; y_start = -1; page_min = -1; page_max = 0; - d = ds_get_data(ts->ds); + d = surface_data(surface); s = ts->vram; - dd = ds_get_linesize(ts->ds); + dd = surface_stride(surface); ds = 1024; - switch (ds_get_bits_per_pixel(ts->ds)) { + switch (surface_bits_per_pixel(surface)) { case 32: f = tcx_draw_line32; break; @@ -269,7 +276,7 @@ static void tcx_update_display(void *opaque) } else { if (y_start >= 0) { /* flush to display */ - dpy_gfx_update(ts->ds, 0, y_start, + dpy_gfx_update(ts->con, 0, y_start, ts->width, y - y_start); y_start = -1; } @@ -279,7 +286,7 @@ static void tcx_update_display(void *opaque) } if (y_start >= 0) { /* flush to display */ - dpy_gfx_update(ts->ds, 0, y_start, + dpy_gfx_update(ts->con, 0, y_start, ts->width, y - y_start); } /* reset modified pages */ @@ -293,24 +300,27 @@ static void tcx_update_display(void *opaque) static void tcx24_update_display(void *opaque) { TCXState *ts = opaque; + DisplaySurface *surface = qemu_console_surface(ts->con); ram_addr_t page, page_min, page_max, cpage, page24; int y, y_start, dd, ds; uint8_t *d, *s; uint32_t *cptr, *s24; - if (ds_get_bits_per_pixel(ts->ds) != 32) + if (surface_bits_per_pixel(surface) != 32) { return; + } + page = 0; page24 = ts->vram24_offset; cpage = ts->cplane_offset; y_start = -1; page_min = -1; page_max = 0; - d = ds_get_data(ts->ds); + d = surface_data(surface); s = ts->vram; s24 = ts->vram24; cptr = ts->cplane; - dd = ds_get_linesize(ts->ds); + dd = surface_stride(surface); ds = 1024; for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE, @@ -345,7 +355,7 @@ static void tcx24_update_display(void *opaque) } else { if (y_start >= 0) { /* flush to display */ - dpy_gfx_update(ts->ds, 0, y_start, + dpy_gfx_update(ts->con, 0, y_start, ts->width, y - y_start); y_start = -1; } @@ -357,7 +367,7 @@ static void tcx24_update_display(void *opaque) } if (y_start >= 0) { /* flush to display */ - dpy_gfx_update(ts->ds, 0, y_start, + dpy_gfx_update(ts->con, 0, y_start, ts->width, y - y_start); } /* reset modified pages */ @@ -371,7 +381,7 @@ static void tcx_invalidate_display(void *opaque) TCXState *s = opaque; tcx_set_dirty(s); - qemu_console_resize(s->ds, s->width, s->height); + qemu_console_resize(s->con, s->width, s->height); } static void tcx24_invalidate_display(void *opaque) @@ -380,7 +390,7 @@ static void tcx24_invalidate_display(void *opaque) tcx_set_dirty(s); tcx24_set_dirty(s); - qemu_console_resize(s->ds, s->width, s->height); + qemu_console_resize(s->con, s->width, s->height); } static int vmstate_tcx_post_load(void *opaque, int version_id) @@ -558,21 +568,21 @@ static int tcx_init1(SysBusDevice *dev) &s->vram_mem, vram_offset, size); sysbus_init_mmio(dev, &s->vram_cplane); - s->ds = graphic_console_init(tcx24_update_display, - tcx24_invalidate_display, - tcx24_screen_dump, NULL, s); + s->con = graphic_console_init(tcx24_update_display, + tcx24_invalidate_display, + tcx24_screen_dump, NULL, s); } else { /* THC 8 bit (dummy) */ memory_region_init_io(&s->thc8, &dummy_ops, s, "tcx.thc8", TCX_THC_NREGS_8); sysbus_init_mmio(dev, &s->thc8); - s->ds = graphic_console_init(tcx_update_display, - tcx_invalidate_display, - tcx_screen_dump, NULL, s); + s->con = graphic_console_init(tcx_update_display, + tcx_invalidate_display, + tcx_screen_dump, NULL, s); } - qemu_console_resize(s->ds, s->width, s->height); + qemu_console_resize(s->con, s->width, s->height); return 0; } diff --git a/hw/vga-isa-mm.c b/hw/vga-isa-mm.c index 4aa62bf35e..02356d4d58 100644 --- a/hw/vga-isa-mm.c +++ b/hw/vga-isa-mm.c @@ -135,8 +135,9 @@ int isa_vga_mm_init(hwaddr vram_base, vga_common_init(&s->vga); vga_mm_init(s, vram_base, ctrl_base, it_shift, address_space); - s->vga.ds = graphic_console_init(s->vga.update, s->vga.invalidate, - s->vga.screen_dump, s->vga.text_update, s); + s->vga.con = graphic_console_init(s->vga.update, s->vga.invalidate, + s->vga.screen_dump, s->vga.text_update, + s); vga_init_vbe(&s->vga, address_space); return 0; diff --git a/hw/vga-isa.c b/hw/vga-isa.c index ffad5226fd..9e293217d0 100644 --- a/hw/vga-isa.c +++ b/hw/vga-isa.c @@ -62,8 +62,8 @@ static int vga_initfn(ISADevice *dev) isa_mem_base + 0x000a0000, vga_io_memory, 1); memory_region_set_coalescing(vga_io_memory); - s->ds = graphic_console_init(s->update, s->invalidate, - s->screen_dump, s->text_update, s); + s->con = graphic_console_init(s->update, s->invalidate, + s->screen_dump, s->text_update, s); vga_init_vbe(s, isa_address_space(dev)); /* ROM BIOS */ diff --git a/hw/vga-pci.c b/hw/vga-pci.c index 18018ff1c3..05fa9bcb64 100644 --- a/hw/vga-pci.c +++ b/hw/vga-pci.c @@ -150,8 +150,8 @@ static int pci_std_vga_initfn(PCIDevice *dev) vga_common_init(s); vga_init(s, pci_address_space(dev), pci_address_space_io(dev), true); - s->ds = graphic_console_init(s->update, s->invalidate, - s->screen_dump, s->text_update, s); + s->con = graphic_console_init(s->update, s->invalidate, + s->screen_dump, s->text_update, s); /* XXX: VGA_RAM_SIZE must be a power of two */ pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram); @@ -1174,9 +1174,9 @@ static int update_basic_params(VGACommonState *s) #define NB_DEPTHS 7 -static inline int get_depth_index(DisplayState *s) +static inline int get_depth_index(DisplaySurface *s) { - switch(ds_get_bits_per_pixel(s)) { + switch (surface_bits_per_pixel(s)) { default: case 8: return 0; @@ -1185,10 +1185,11 @@ static inline int get_depth_index(DisplayState *s) case 16: return 2; case 32: - if (is_surface_bgr(s->surface)) + if (is_surface_bgr(s)) { return 4; - else + } else { return 3; + } } } @@ -1294,6 +1295,7 @@ static rgb_to_pixel_dup_func * const rgb_to_pixel_dup_table[NB_DEPTHS] = { */ static void vga_draw_text(VGACommonState *s, int full_update) { + DisplaySurface *surface = qemu_console_surface(s->con); int cx, cy, cheight, cw, ch, cattr, height, width, ch_attr; int cx_min, cx_max, linesize, x_incr, line, line1; uint32_t offset, fgcol, bgcol, v, cursor_offset; @@ -1345,8 +1347,9 @@ static void vga_draw_text(VGACommonState *s, int full_update) cw != s->last_cw || cheight != s->last_ch || s->last_depth) { 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); - dpy_text_resize(s->ds, width, height); + qemu_console_resize(s->con, s->last_scr_width, s->last_scr_height); + surface = qemu_console_surface(s->con); + dpy_text_resize(s->con, width, height); s->last_depth = 0; s->last_width = width; s->last_height = height; @@ -1355,10 +1358,10 @@ static void vga_draw_text(VGACommonState *s, int full_update) full_update = 1; } s->rgb_to_pixel = - rgb_to_pixel_dup_table[get_depth_index(s->ds)]; + rgb_to_pixel_dup_table[get_depth_index(surface)]; full_update |= update_palette16(s); palette = s->last_palette; - x_incr = cw * ((ds_get_bits_per_pixel(s->ds) + 7) >> 3); + x_incr = cw * surface_bytes_per_pixel(surface); if (full_update) { s->full_update_text = 1; @@ -1389,15 +1392,15 @@ static void vga_draw_text(VGACommonState *s, int full_update) s->cursor_visible_phase = !s->cursor_visible_phase; } - depth_index = get_depth_index(s->ds); + depth_index = get_depth_index(surface); if (cw == 16) vga_draw_glyph8 = vga_draw_glyph16_table[depth_index]; else vga_draw_glyph8 = vga_draw_glyph8_table[depth_index]; vga_draw_glyph9 = vga_draw_glyph9_table[depth_index]; - dest = ds_get_data(s->ds); - linesize = ds_get_linesize(s->ds); + dest = surface_data(surface); + linesize = surface_stride(surface); ch_attr_ptr = s->last_ch_attr; line = 0; offset = s->start_addr * 4; @@ -1465,7 +1468,7 @@ static void vga_draw_text(VGACommonState *s, int full_update) ch_attr_ptr++; } if (cx_max != -1) { - dpy_gfx_update(s->ds, cx_min * cw, cy * cheight, + dpy_gfx_update(s->con, cx_min * cw, cy * cheight, (cx_max - cx_min + 1) * cw, cheight); } dest += linesize * cheight; @@ -1636,6 +1639,7 @@ void vga_dirty_log_stop(VGACommonState *s) */ static void vga_draw_graphic(VGACommonState *s, int full_update) { + DisplaySurface *surface = qemu_console_surface(s->con); int y1, y, update, linesize, y_start, double_scan, mask, depth; int width, height, shift_control, line_offset, bwidth, bits; ram_addr_t page0, page1, page_min, page_max; @@ -1691,13 +1695,13 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) height != s->last_height || s->last_depth != depth) { if (depth == 32 || (depth == 16 && !byteswap)) { - qemu_free_displaysurface(s->ds); - s->ds->surface = qemu_create_displaysurface_from(disp_width, height, depth, - s->line_offset, + surface = qemu_create_displaysurface_from(disp_width, + height, depth, s->line_offset, s->vram_ptr + (s->start_addr * 4), byteswap); - dpy_gfx_resize(s->ds); + dpy_gfx_replace_surface(s->con, surface); } else { - qemu_console_resize(s->ds, disp_width, height); + qemu_console_resize(s->con, disp_width, height); + surface = qemu_console_surface(s->con); } s->last_scr_width = disp_width; s->last_scr_height = height; @@ -1706,19 +1710,18 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) s->last_line_offset = s->line_offset; s->last_depth = depth; full_update = 1; - } else if (is_buffer_shared(s->ds->surface) && - (full_update || ds_get_data(s->ds) != s->vram_ptr + } else if (is_buffer_shared(surface) && + (full_update || surface_data(surface) != s->vram_ptr + (s->start_addr * 4))) { - qemu_free_displaysurface(s->ds); - s->ds->surface = qemu_create_displaysurface_from(disp_width, - height, depth, - s->line_offset, + DisplaySurface *surface; + surface = qemu_create_displaysurface_from(disp_width, + height, depth, s->line_offset, s->vram_ptr + (s->start_addr * 4), byteswap); - dpy_gfx_setdata(s->ds); + dpy_gfx_replace_surface(s->con, surface); } s->rgb_to_pixel = - rgb_to_pixel_dup_table[get_depth_index(s->ds)]; + rgb_to_pixel_dup_table[get_depth_index(surface)]; if (shift_control == 0) { full_update |= update_palette16(s); @@ -1767,10 +1770,12 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) break; } } - vga_draw_line = vga_draw_line_table[v * NB_DEPTHS + get_depth_index(s->ds)]; + vga_draw_line = vga_draw_line_table[v * NB_DEPTHS + + get_depth_index(surface)]; - if (!is_buffer_shared(s->ds->surface) && s->cursor_invalidate) + if (!is_buffer_shared(surface) && s->cursor_invalidate) { s->cursor_invalidate(s); + } line_offset = s->line_offset; #if 0 @@ -1783,8 +1788,8 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) y_start = -1; page_min = -1; page_max = 0; - d = ds_get_data(s->ds); - linesize = ds_get_linesize(s->ds); + d = surface_data(surface); + linesize = surface_stride(surface); y1 = 0; for(y = 0; y < height; y++) { addr = addr1; @@ -1811,7 +1816,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) page_min = page0; if (page1 > page_max) page_max = page1; - if (!(is_buffer_shared(s->ds->surface))) { + if (!(is_buffer_shared(surface))) { vga_draw_line(s, d, s->vram_ptr + addr, width); if (s->cursor_draw_line) s->cursor_draw_line(s, d, y); @@ -1819,7 +1824,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) } else { if (y_start >= 0) { /* flush to display */ - dpy_gfx_update(s->ds, 0, y_start, + dpy_gfx_update(s->con, 0, y_start, disp_width, y - y_start); y_start = -1; } @@ -1840,7 +1845,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) } if (y_start >= 0) { /* flush to display */ - dpy_gfx_update(s->ds, 0, y_start, + dpy_gfx_update(s->con, 0, y_start, disp_width, y - y_start); } /* reset modified pages */ @@ -1855,6 +1860,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) static void vga_draw_blank(VGACommonState *s, int full_update) { + DisplaySurface *surface = qemu_console_surface(s->con); int i, w, val; uint8_t *d; @@ -1864,18 +1870,19 @@ static void vga_draw_blank(VGACommonState *s, int full_update) return; s->rgb_to_pixel = - rgb_to_pixel_dup_table[get_depth_index(s->ds)]; - if (ds_get_bits_per_pixel(s->ds) == 8) + rgb_to_pixel_dup_table[get_depth_index(surface)]; + if (surface_bits_per_pixel(surface) == 8) { val = s->rgb_to_pixel(0, 0, 0); - else + } else { val = 0; - w = s->last_scr_width * ((ds_get_bits_per_pixel(s->ds) + 7) >> 3); - d = ds_get_data(s->ds); + } + w = s->last_scr_width * surface_bytes_per_pixel(surface); + d = surface_data(surface); for(i = 0; i < s->last_scr_height; i++) { memset(d, val, w); - d += ds_get_linesize(s->ds); + d += surface_stride(surface); } - dpy_gfx_update(s->ds, 0, 0, + dpy_gfx_update(s->con, 0, 0, s->last_scr_width, s->last_scr_height); } @@ -1886,11 +1893,12 @@ static void vga_draw_blank(VGACommonState *s, int full_update) static void vga_update_display(void *opaque) { VGACommonState *s = opaque; + DisplaySurface *surface = qemu_console_surface(s->con); int full_update, graphic_mode; qemu_flush_coalesced_mmio_buffer(); - if (ds_get_bits_per_pixel(s->ds) == 0) { + if (surface_bits_per_pixel(surface) == 0) { /* nothing to do */ } else { full_update = 0; @@ -2064,8 +2072,8 @@ static void vga_update_text(void *opaque, console_ch_t *chardata) cw != s->last_cw || cheight != s->last_ch) { 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); - dpy_text_resize(s->ds, width, height); + qemu_console_resize(s->con, s->last_scr_width, s->last_scr_height); + dpy_text_resize(s->con, width, height); s->last_depth = 0; s->last_width = width; s->last_height = height; @@ -2090,11 +2098,11 @@ static void vga_update_text(void *opaque, console_ch_t *chardata) s->cr[VGA_CRTC_CURSOR_END] != s->cursor_end || full_update) { cursor_visible = !(s->cr[VGA_CRTC_CURSOR_START] & 0x20); if (cursor_visible && cursor_offset < size && cursor_offset >= 0) - dpy_text_cursor(s->ds, + dpy_text_cursor(s->con, TEXTMODE_X(cursor_offset), TEXTMODE_Y(cursor_offset)); else - dpy_text_cursor(s->ds, -1, -1); + dpy_text_cursor(s->con, -1, -1); s->cursor_offset = cursor_offset; s->cursor_start = s->cr[VGA_CRTC_CURSOR_START]; s->cursor_end = s->cr[VGA_CRTC_CURSOR_END]; @@ -2107,7 +2115,7 @@ static void vga_update_text(void *opaque, console_ch_t *chardata) for (i = 0; i < size; src ++, dst ++, i ++) console_write_ch(dst, VMEM2CHTYPE(le32_to_cpu(*src))); - dpy_text_update(s->ds, 0, 0, width, height); + dpy_text_update(s->con, 0, 0, width, height); } else { c_max = 0; @@ -2130,7 +2138,7 @@ static void vga_update_text(void *opaque, console_ch_t *chardata) if (c_min <= c_max) { i = TEXTMODE_Y(c_min); - dpy_text_update(s->ds, 0, i, width, TEXTMODE_Y(c_max) - i + 1); + dpy_text_update(s->con, 0, i, width, TEXTMODE_Y(c_max) - i + 1); } } @@ -2155,8 +2163,8 @@ static void vga_update_text(void *opaque, console_ch_t *chardata) /* Display a message */ s->last_width = 60; s->last_height = height = 3; - dpy_text_cursor(s->ds, -1, -1); - dpy_text_resize(s->ds, s->last_width, height); + dpy_text_cursor(s->con, -1, -1); + dpy_text_resize(s->con, s->last_width, height); for (dst = chardata, i = 0; i < s->last_width * height; i ++) console_write_ch(dst ++, ' '); @@ -2167,7 +2175,7 @@ static void vga_update_text(void *opaque, console_ch_t *chardata) for (i = 0; i < size; i ++) console_write_ch(dst ++, 0x00200100 | msg_buffer[i]); - dpy_text_update(s->ds, 0, 0, s->last_width, height); + dpy_text_update(s->con, 0, 0, s->last_width, height); } static uint64_t vga_mem_read(void *opaque, hwaddr addr, @@ -2439,10 +2447,11 @@ static void vga_screen_dump(void *opaque, const char *filename, bool cswitch, Error **errp) { VGACommonState *s = opaque; + DisplaySurface *surface = qemu_console_surface(s->con); if (cswitch) { vga_invalidate_display(s); } vga_hw_update(); - ppm_save(filename, s->ds->surface, errp); + ppm_save(filename, surface, errp); } diff --git a/hw/vga_int.h b/hw/vga_int.h index 8d496ea9bf..260f7d6948 100644 --- a/hw/vga_int.h +++ b/hw/vga_int.h @@ -132,7 +132,7 @@ typedef struct VGACommonState { uint32_t vbe_bank_mask; int vbe_mapped; /* display refresh support */ - DisplayState *ds; + QemuConsole *con; uint32_t font_offsets[2]; int graphic_mode; uint8_t shift_control; diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c index 6bfcddc379..54a43728a5 100644 --- a/hw/virtio-balloon.c +++ b/hw/virtio-balloon.c @@ -29,21 +29,6 @@ #include <sys/mman.h> #endif -typedef struct VirtIOBalloon -{ - VirtIODevice vdev; - VirtQueue *ivq, *dvq, *svq; - uint32_t num_pages; - uint32_t actual; - uint64_t stats[VIRTIO_BALLOON_S_NR]; - VirtQueueElement stats_vq_elem; - size_t stats_vq_offset; - QEMUTimer *stats_timer; - int64_t stats_last_update; - int64_t stats_poll_interval; - DeviceState *qdev; -} VirtIOBalloon; - static VirtIOBalloon *to_virtio_balloon(VirtIODevice *vdev) { return (VirtIOBalloon *)vdev; diff --git a/hw/virtio-balloon.h b/hw/virtio-balloon.h index f37f31b4d7..b0070421ca 100644 --- a/hw/virtio-balloon.h +++ b/hw/virtio-balloon.h @@ -52,4 +52,18 @@ typedef struct VirtIOBalloonStat { uint64_t val; } QEMU_PACKED VirtIOBalloonStat; +typedef struct VirtIOBalloon { + VirtIODevice vdev; + VirtQueue *ivq, *dvq, *svq; + uint32_t num_pages; + uint32_t actual; + uint64_t stats[VIRTIO_BALLOON_S_NR]; + VirtQueueElement stats_vq_elem; + size_t stats_vq_offset; + QEMUTimer *stats_timer; + int64_t stats_last_update; + int64_t stats_poll_interval; + DeviceState *qdev; +} VirtIOBalloon; + #endif diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c index 6b69236655..f2143fded3 100644 --- a/hw/virtio-blk.c +++ b/hw/virtio-blk.c @@ -17,35 +17,11 @@ #include "hw/block-common.h" #include "sysemu/blockdev.h" #include "hw/virtio-blk.h" -#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE -#include "dataplane/virtio-blk.h" -#endif #include "hw/scsi-defs.h" #ifdef __linux__ # include <scsi/sg.h> #endif - -typedef struct VirtIOBlock -{ - VirtIODevice vdev; - BlockDriverState *bs; - VirtQueue *vq; - void *rq; - QEMUBH *bh; - BlockConf *conf; - VirtIOBlkConf *blk; - unsigned short sector_mask; - DeviceState *qdev; - VMChangeStateEntry *change; -#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE - VirtIOBlockDataPlane *dataplane; -#endif -} VirtIOBlock; - -static VirtIOBlock *to_virtio_blk(VirtIODevice *vdev) -{ - return (VirtIOBlock *)vdev; -} +#include "hw/virtio-bus.h" typedef struct VirtIOBlockReq { @@ -62,12 +38,13 @@ typedef struct VirtIOBlockReq static void virtio_blk_req_complete(VirtIOBlockReq *req, int status) { VirtIOBlock *s = req->dev; + VirtIODevice *vdev = VIRTIO_DEVICE(s); trace_virtio_blk_req_complete(req, status); stb_p(&req->in->status, status); virtqueue_push(s->vq, &req->elem, req->qiov.size + sizeof(*req->in)); - virtio_notify(&s->vdev, s->vq); + virtio_notify(vdev, s->vq); } static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error, @@ -171,7 +148,7 @@ static void virtio_blk_handle_scsi(VirtIOBlockReq *req) */ req->scsi = (void *)req->elem.in_sg[req->elem.in_num - 2].iov_base; - if (!req->dev->blk->scsi) { + if (!req->dev->blk.scsi) { status = VIRTIO_BLK_S_UNSUPP; goto fail; } @@ -391,7 +368,7 @@ static void virtio_blk_handle_request(VirtIOBlockReq *req, * terminated by '\0' only when shorter than buffer. */ strncpy(req->elem.in_sg[0].iov_base, - s->blk->serial ? s->blk->serial : "", + s->blk.serial ? s->blk.serial : "", MIN(req->elem.in_sg[0].iov_len, VIRTIO_BLK_ID_BYTES)); virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); g_free(req); @@ -412,7 +389,7 @@ static void virtio_blk_handle_request(VirtIOBlockReq *req, static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) { - VirtIOBlock *s = to_virtio_blk(vdev); + VirtIOBlock *s = VIRTIO_BLK(vdev); VirtIOBlockReq *req; MultiReqBuffer mrb = { .num_writes = 0, @@ -480,7 +457,7 @@ static void virtio_blk_dma_restart_cb(void *opaque, int running, static void virtio_blk_reset(VirtIODevice *vdev) { #ifdef CONFIG_VIRTIO_BLK_DATA_PLANE - VirtIOBlock *s = to_virtio_blk(vdev); + VirtIOBlock *s = VIRTIO_BLK(vdev); if (s->dataplane) { virtio_blk_data_plane_stop(s->dataplane); @@ -498,7 +475,7 @@ static void virtio_blk_reset(VirtIODevice *vdev) */ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) { - VirtIOBlock *s = to_virtio_blk(vdev); + VirtIOBlock *s = VIRTIO_BLK(vdev); struct virtio_blk_config blkcfg; uint64_t capacity; int blk_size = s->conf->logical_block_size; @@ -537,7 +514,7 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) static void virtio_blk_set_config(VirtIODevice *vdev, const uint8_t *config) { - VirtIOBlock *s = to_virtio_blk(vdev); + VirtIOBlock *s = VIRTIO_BLK(vdev); struct virtio_blk_config blkcfg; memcpy(&blkcfg, config, sizeof(blkcfg)); @@ -546,7 +523,7 @@ static void virtio_blk_set_config(VirtIODevice *vdev, const uint8_t *config) static uint32_t virtio_blk_get_features(VirtIODevice *vdev, uint32_t features) { - VirtIOBlock *s = to_virtio_blk(vdev); + VirtIOBlock *s = VIRTIO_BLK(vdev); features |= (1 << VIRTIO_BLK_F_SEG_MAX); features |= (1 << VIRTIO_BLK_F_GEOMETRY); @@ -554,7 +531,7 @@ static uint32_t virtio_blk_get_features(VirtIODevice *vdev, uint32_t features) features |= (1 << VIRTIO_BLK_F_BLK_SIZE); features |= (1 << VIRTIO_BLK_F_SCSI); - if (s->blk->config_wce) { + if (s->blk.config_wce) { features |= (1 << VIRTIO_BLK_F_CONFIG_WCE); } if (bdrv_enable_write_cache(s->bs)) @@ -568,7 +545,7 @@ static uint32_t virtio_blk_get_features(VirtIODevice *vdev, uint32_t features) static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) { - VirtIOBlock *s = to_virtio_blk(vdev); + VirtIOBlock *s = VIRTIO_BLK(vdev); uint32_t features; #ifdef CONFIG_VIRTIO_BLK_DATA_PLANE @@ -589,9 +566,10 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) static void virtio_blk_save(QEMUFile *f, void *opaque) { VirtIOBlock *s = opaque; + VirtIODevice *vdev = VIRTIO_DEVICE(s); VirtIOBlockReq *req = s->rq; - virtio_save(&s->vdev, f); + virtio_save(vdev, f); while (req) { qemu_put_sbyte(f, 1); @@ -604,12 +582,13 @@ static void virtio_blk_save(QEMUFile *f, void *opaque) static int virtio_blk_load(QEMUFile *f, void *opaque, int version_id) { VirtIOBlock *s = opaque; + VirtIODevice *vdev = VIRTIO_DEVICE(s); int ret; if (version_id != 2) return -EINVAL; - ret = virtio_load(&s->vdev, f); + ret = virtio_load(vdev, f); if (ret) { return ret; } @@ -631,80 +610,120 @@ static int virtio_blk_load(QEMUFile *f, void *opaque, int version_id) static void virtio_blk_resize(void *opaque) { - VirtIOBlock *s = opaque; + VirtIODevice *vdev = VIRTIO_DEVICE(opaque); - virtio_notify_config(&s->vdev); + virtio_notify_config(vdev); } static const BlockDevOps virtio_block_ops = { .resize_cb = virtio_blk_resize, }; -VirtIODevice *virtio_blk_init(DeviceState *dev, VirtIOBlkConf *blk) +void virtio_blk_set_conf(DeviceState *dev, VirtIOBlkConf *blk) { - VirtIOBlock *s; + VirtIOBlock *s = VIRTIO_BLK(dev); + memcpy(&(s->blk), blk, sizeof(struct VirtIOBlkConf)); +} + +static int virtio_blk_device_init(VirtIODevice *vdev) +{ + DeviceState *qdev = DEVICE(vdev); + VirtIOBlock *s = VIRTIO_BLK(vdev); + VirtIOBlkConf *blk = &(s->blk); static int virtio_blk_id; if (!blk->conf.bs) { error_report("drive property not set"); - return NULL; + return -1; } if (!bdrv_is_inserted(blk->conf.bs)) { error_report("Device needs media, but drive is empty"); - return NULL; + return -1; } blkconf_serial(&blk->conf, &blk->serial); if (blkconf_geometry(&blk->conf, NULL, 65535, 255, 255) < 0) { - return NULL; + return -1; } - s = (VirtIOBlock *)virtio_common_init("virtio-blk", VIRTIO_ID_BLOCK, - sizeof(struct virtio_blk_config), - sizeof(VirtIOBlock)); + virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK, + sizeof(struct virtio_blk_config)); - s->vdev.get_config = virtio_blk_update_config; - s->vdev.set_config = virtio_blk_set_config; - s->vdev.get_features = virtio_blk_get_features; - s->vdev.set_status = virtio_blk_set_status; - s->vdev.reset = virtio_blk_reset; + vdev->get_config = virtio_blk_update_config; + vdev->set_config = virtio_blk_set_config; + vdev->get_features = virtio_blk_get_features; + vdev->set_status = virtio_blk_set_status; + vdev->reset = virtio_blk_reset; s->bs = blk->conf.bs; s->conf = &blk->conf; - s->blk = blk; + memcpy(&(s->blk), blk, sizeof(struct VirtIOBlkConf)); s->rq = NULL; s->sector_mask = (s->conf->logical_block_size / BDRV_SECTOR_SIZE) - 1; - s->vq = virtio_add_queue(&s->vdev, 128, virtio_blk_handle_output); + s->vq = virtio_add_queue(vdev, 128, virtio_blk_handle_output); #ifdef CONFIG_VIRTIO_BLK_DATA_PLANE - if (!virtio_blk_data_plane_create(&s->vdev, blk, &s->dataplane)) { - virtio_cleanup(&s->vdev); - return NULL; + if (!virtio_blk_data_plane_create(vdev, blk, &s->dataplane)) { + virtio_common_cleanup(vdev); + return -1; } #endif s->change = qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s); - s->qdev = dev; - register_savevm(dev, "virtio-blk", virtio_blk_id++, 2, + register_savevm(qdev, "virtio-blk", virtio_blk_id++, 2, virtio_blk_save, virtio_blk_load, s); bdrv_set_dev_ops(s->bs, &virtio_block_ops, s); bdrv_set_buffer_alignment(s->bs, s->conf->logical_block_size); bdrv_iostatus_enable(s->bs); - add_boot_device_path(s->conf->bootindex, dev, "/disk@0,0"); - return &s->vdev; + add_boot_device_path(s->conf->bootindex, qdev, "/disk@0,0"); + return 0; } -void virtio_blk_exit(VirtIODevice *vdev) +static int virtio_blk_device_exit(DeviceState *dev) { - VirtIOBlock *s = to_virtio_blk(vdev); - + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VirtIOBlock *s = VIRTIO_BLK(dev); #ifdef CONFIG_VIRTIO_BLK_DATA_PLANE virtio_blk_data_plane_destroy(s->dataplane); s->dataplane = NULL; #endif qemu_del_vm_change_state_handler(s->change); - unregister_savevm(s->qdev, "virtio-blk", s); + unregister_savevm(dev, "virtio-blk", s); blockdev_mark_auto_del(s->bs); - virtio_cleanup(vdev); + virtio_common_cleanup(vdev); + return 0; +} + +static Property virtio_blk_properties[] = { + DEFINE_VIRTIO_BLK_PROPERTIES(VirtIOBlock, blk), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_blk_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + dc->exit = virtio_blk_device_exit; + dc->props = virtio_blk_properties; + vdc->init = virtio_blk_device_init; + vdc->get_config = virtio_blk_update_config; + vdc->set_config = virtio_blk_set_config; + vdc->get_features = virtio_blk_get_features; + vdc->set_status = virtio_blk_set_status; + vdc->reset = virtio_blk_reset; } + +static const TypeInfo virtio_device_info = { + .name = TYPE_VIRTIO_BLK, + .parent = TYPE_VIRTIO_DEVICE, + .instance_size = sizeof(VirtIOBlock), + .class_init = virtio_blk_class_init, +}; + +static void virtio_register_types(void) +{ + type_register_static(&virtio_device_info); +} + +type_init(virtio_register_types) diff --git a/hw/virtio-blk.h b/hw/virtio-blk.h index 7ef2f35852..8c6c78b191 100644 --- a/hw/virtio-blk.h +++ b/hw/virtio-blk.h @@ -16,6 +16,13 @@ #include "hw/virtio.h" #include "hw/block-common.h" +#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE +#include "dataplane/virtio-blk.h" +#endif + +#define TYPE_VIRTIO_BLK "virtio-blk" +#define VIRTIO_BLK(obj) \ + OBJECT_CHECK(VirtIOBlock, (obj), TYPE_VIRTIO_BLK) /* from Linux's linux/virtio_blk.h */ @@ -108,7 +115,39 @@ struct VirtIOBlkConf uint32_t data_plane; }; +typedef struct VirtIOBlock { + VirtIODevice parent_obj; + BlockDriverState *bs; + VirtQueue *vq; + void *rq; + QEMUBH *bh; + BlockConf *conf; + VirtIOBlkConf blk; + unsigned short sector_mask; + VMChangeStateEntry *change; +#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE + VirtIOBlockDataPlane *dataplane; +#endif +} VirtIOBlock; + #define DEFINE_VIRTIO_BLK_FEATURES(_state, _field) \ DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) +#ifdef __linux__ +#define DEFINE_VIRTIO_BLK_PROPERTIES(_state, _field) \ + DEFINE_BLOCK_PROPERTIES(_state, _field.conf), \ + DEFINE_BLOCK_CHS_PROPERTIES(_state, _field.conf), \ + DEFINE_PROP_STRING("serial", _state, _field.serial), \ + DEFINE_PROP_BIT("config-wce", _state, _field.config_wce, 0, true), \ + DEFINE_PROP_BIT("scsi", _state, _field.scsi, 0, true) +#else +#define DEFINE_VIRTIO_BLK_PROPERTIES(_state, _field) \ + DEFINE_BLOCK_PROPERTIES(_state, _field.conf), \ + DEFINE_BLOCK_CHS_PROPERTIES(_state, _field.conf), \ + DEFINE_PROP_STRING("serial", _state, _field.serial), \ + DEFINE_PROP_BIT("config-wce", _state, _field.config_wce, 0, true) +#endif /* __linux__ */ + +void virtio_blk_set_conf(DeviceState *dev, VirtIOBlkConf *blk); + #endif diff --git a/hw/virtio-net.c b/hw/virtio-net.c index 4590557352..5917740d9d 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -26,56 +26,6 @@ #define MAC_TABLE_ENTRIES 64 #define MAX_VLAN (1 << 12) /* Per 802.1Q definition */ -typedef struct VirtIONetQueue { - VirtQueue *rx_vq; - VirtQueue *tx_vq; - QEMUTimer *tx_timer; - QEMUBH *tx_bh; - int tx_waiting; - struct { - VirtQueueElement elem; - ssize_t len; - } async_tx; - struct VirtIONet *n; -} VirtIONetQueue; - -typedef struct VirtIONet -{ - VirtIODevice vdev; - uint8_t mac[ETH_ALEN]; - uint16_t status; - VirtIONetQueue *vqs; - VirtQueue *ctrl_vq; - NICState *nic; - uint32_t tx_timeout; - int32_t tx_burst; - uint32_t has_vnet_hdr; - size_t host_hdr_len; - size_t guest_hdr_len; - uint8_t has_ufo; - int mergeable_rx_bufs; - uint8_t promisc; - uint8_t allmulti; - uint8_t alluni; - uint8_t nomulti; - uint8_t nouni; - uint8_t nobcast; - uint8_t vhost_started; - struct { - int in_use; - int first_multi; - uint8_t multi_overflow; - uint8_t uni_overflow; - uint8_t *macs; - } mac_table; - uint32_t *vlans; - DeviceState *qdev; - int multiqueue; - uint16_t max_queues; - uint16_t curr_queues; - size_t config_size; -} VirtIONet; - /* * Calculate the number of bytes up to and including the given 'field' of * 'container'. diff --git a/hw/virtio-net.h b/hw/virtio-net.h index 0c83ca5cfe..4d1a8cdca0 100644 --- a/hw/virtio-net.h +++ b/hw/virtio-net.h @@ -134,6 +134,56 @@ struct virtio_net_ctrl_mac { uint32_t entries; uint8_t macs[][ETH_ALEN]; }; + +typedef struct VirtIONetQueue { + VirtQueue *rx_vq; + VirtQueue *tx_vq; + QEMUTimer *tx_timer; + QEMUBH *tx_bh; + int tx_waiting; + struct { + VirtQueueElement elem; + ssize_t len; + } async_tx; + struct VirtIONet *n; +} VirtIONetQueue; + +typedef struct VirtIONet { + VirtIODevice vdev; + uint8_t mac[ETH_ALEN]; + uint16_t status; + VirtIONetQueue *vqs; + VirtQueue *ctrl_vq; + NICState *nic; + uint32_t tx_timeout; + int32_t tx_burst; + uint32_t has_vnet_hdr; + size_t host_hdr_len; + size_t guest_hdr_len; + uint8_t has_ufo; + int mergeable_rx_bufs; + uint8_t promisc; + uint8_t allmulti; + uint8_t alluni; + uint8_t nomulti; + uint8_t nouni; + uint8_t nobcast; + uint8_t vhost_started; + struct { + int in_use; + int first_multi; + uint8_t multi_overflow; + uint8_t uni_overflow; + uint8_t *macs; + } mac_table; + uint32_t *vlans; + DeviceState *qdev; + int multiqueue; + uint16_t max_queues; + uint16_t curr_queues; + size_t config_size; +} VirtIONet; + #define VIRTIO_NET_CTRL_MAC 1 #define VIRTIO_NET_CTRL_MAC_TABLE_SET 0 #define VIRTIO_NET_CTRL_MAC_ADDR_SET 1 diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c index 19965e5a31..736a9bf07d 100644 --- a/hw/virtio-pci.c +++ b/hw/virtio-pci.c @@ -933,26 +933,6 @@ void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev) proxy->host_features = vdev->get_features(vdev, proxy->host_features); } -static int virtio_blk_init_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - VirtIODevice *vdev; - - if (proxy->class_code != PCI_CLASS_STORAGE_SCSI && - proxy->class_code != PCI_CLASS_STORAGE_OTHER) - proxy->class_code = PCI_CLASS_STORAGE_SCSI; - - vdev = virtio_blk_init(&pci_dev->qdev, &proxy->blk); - if (!vdev) { - return -1; - } - vdev->nvectors = proxy->nvectors; - virtio_init_pci(proxy, vdev); - /* make the actual value visible */ - proxy->nvectors = vdev->nvectors; - return 0; -} - static void virtio_exit_pci(PCIDevice *pci_dev) { VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); @@ -961,15 +941,6 @@ static void virtio_exit_pci(PCIDevice *pci_dev) msix_uninit_exclusive_bar(pci_dev); } -static void virtio_blk_exit_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - - virtio_pci_stop_ioeventfd(proxy); - virtio_blk_exit(proxy->vdev); - virtio_exit_pci(pci_dev); -} - static int virtio_serial_init_pci(PCIDevice *pci_dev) { VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); @@ -1091,46 +1062,6 @@ static void virtio_rng_exit_pci(PCIDevice *pci_dev) virtio_exit_pci(pci_dev); } -static Property virtio_blk_properties[] = { - DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0), - DEFINE_BLOCK_PROPERTIES(VirtIOPCIProxy, blk.conf), - DEFINE_BLOCK_CHS_PROPERTIES(VirtIOPCIProxy, blk.conf), - DEFINE_PROP_STRING("serial", VirtIOPCIProxy, blk.serial), -#ifdef __linux__ - DEFINE_PROP_BIT("scsi", VirtIOPCIProxy, blk.scsi, 0, true), -#endif - DEFINE_PROP_BIT("config-wce", VirtIOPCIProxy, blk.config_wce, 0, true), - DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), -#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE - DEFINE_PROP_BIT("x-data-plane", VirtIOPCIProxy, blk.data_plane, 0, false), -#endif - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), - DEFINE_VIRTIO_BLK_FEATURES(VirtIOPCIProxy, host_features), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_blk_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = virtio_blk_init_pci; - k->exit = virtio_blk_exit_pci; - k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - k->device_id = PCI_DEVICE_ID_VIRTIO_BLOCK; - k->revision = VIRTIO_PCI_ABI_VERSION; - k->class_id = PCI_CLASS_STORAGE_SCSI; - dc->reset = virtio_pci_reset; - dc->props = virtio_blk_properties; -} - -static const TypeInfo virtio_blk_info = { - .name = "virtio-blk-pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(VirtIOPCIProxy), - .class_init = virtio_blk_class_init, -}; - static Property virtio_net_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, false), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3), @@ -1149,7 +1080,7 @@ static void virtio_net_class_init(ObjectClass *klass, void *data) k->init = virtio_net_init_pci; k->exit = virtio_net_exit_pci; - k->romfile = "pxe-virtio.rom"; + k->romfile = "efi-virtio.rom"; k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; k->device_id = PCI_DEVICE_ID_VIRTIO_NET; k->revision = VIRTIO_PCI_ABI_VERSION; @@ -1268,63 +1199,6 @@ static const TypeInfo virtio_rng_info = { .class_init = virtio_rng_class_init, }; -static int virtio_scsi_init_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - VirtIODevice *vdev; - - vdev = virtio_scsi_init(&pci_dev->qdev, &proxy->scsi); - if (!vdev) { - return -EINVAL; - } - - vdev->nvectors = proxy->nvectors == DEV_NVECTORS_UNSPECIFIED - ? proxy->scsi.num_queues + 3 - : proxy->nvectors; - virtio_init_pci(proxy, vdev); - - /* make the actual value visible */ - proxy->nvectors = vdev->nvectors; - return 0; -} - -static void virtio_scsi_exit_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - - virtio_scsi_exit(proxy->vdev); - virtio_exit_pci(pci_dev); -} - -static Property virtio_scsi_properties[] = { - DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), - DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOPCIProxy, host_features, scsi), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_scsi_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = virtio_scsi_init_pci; - k->exit = virtio_scsi_exit_pci; - k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI; - k->revision = 0x00; - k->class_id = PCI_CLASS_STORAGE_SCSI; - dc->reset = virtio_pci_reset; - dc->props = virtio_scsi_properties; -} - -static const TypeInfo virtio_scsi_info = { - .name = "virtio-scsi-pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(VirtIOPCIProxy), - .class_init = virtio_scsi_class_init, -}; - #ifdef CONFIG_VIRTFS static int virtio_9p_init_pci(PCIDevice *pci_dev) { @@ -1421,13 +1295,6 @@ static void virtio_pci_device_plugged(DeviceState *d) proxy->host_features); } -/* This is called by virtio-bus just before the device is unplugged. */ -static void virtio_pci_device_unplug(DeviceState *d) -{ - VirtIOPCIProxy *dev = VIRTIO_PCI(d); - virtio_pci_stop_ioeventfd(dev); -} - static int virtio_pci_init(PCIDevice *pci_dev) { VirtIOPCIProxy *dev = VIRTIO_PCI(pci_dev); @@ -1442,10 +1309,7 @@ static int virtio_pci_init(PCIDevice *pci_dev) static void virtio_pci_exit(PCIDevice *pci_dev) { VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev); - VirtioBusState *bus = VIRTIO_BUS(&proxy->bus); - BusState *qbus = BUS(&proxy->bus); - virtio_bus_destroy_device(bus); - qbus_free(qbus); + virtio_pci_stop_ioeventfd(proxy); virtio_exit_pci(pci_dev); } @@ -1485,6 +1349,118 @@ static const TypeInfo virtio_pci_info = { .abstract = true, }; +/* virtio-blk-pci */ + +static Property virtio_blk_pci_properties[] = { + DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0), + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), +#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE + DEFINE_PROP_BIT("x-data-plane", VirtIOBlkPCI, blk.data_plane, 0, false), +#endif + DEFINE_VIRTIO_BLK_FEATURES(VirtIOPCIProxy, host_features), + DEFINE_VIRTIO_BLK_PROPERTIES(VirtIOBlkPCI, blk), + DEFINE_PROP_END_OF_LIST(), +}; + +static int virtio_blk_pci_init(VirtIOPCIProxy *vpci_dev) +{ + VirtIOBlkPCI *dev = VIRTIO_BLK_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + virtio_blk_set_conf(vdev, &(dev->blk)); + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); + if (qdev_init(vdev) < 0) { + return -1; + } + return 0; +} + +static void virtio_blk_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + + dc->props = virtio_blk_pci_properties; + k->init = virtio_blk_pci_init; + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BLOCK; + pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; + pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI; +} + +static void virtio_blk_pci_instance_init(Object *obj) +{ + VirtIOBlkPCI *dev = VIRTIO_BLK_PCI(obj); + object_initialize(OBJECT(&dev->vdev), TYPE_VIRTIO_BLK); + object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL); +} + +static const TypeInfo virtio_blk_pci_info = { + .name = TYPE_VIRTIO_BLK_PCI, + .parent = TYPE_VIRTIO_PCI, + .instance_size = sizeof(VirtIOBlkPCI), + .instance_init = virtio_blk_pci_instance_init, + .class_init = virtio_blk_pci_class_init, +}; + +/* virtio-scsi-pci */ + +static Property virtio_scsi_pci_properties[] = { + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, + DEV_NVECTORS_UNSPECIFIED), + DEFINE_VIRTIO_SCSI_FEATURES(VirtIOPCIProxy, host_features), + DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOSCSIPCI, vdev.conf), + DEFINE_PROP_END_OF_LIST(), +}; + +static int virtio_scsi_pci_init_pci(VirtIOPCIProxy *vpci_dev) +{ + VirtIOSCSIPCI *dev = VIRTIO_SCSI_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { + vpci_dev->nvectors = dev->vdev.conf.num_queues + 3; + } + + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); + if (qdev_init(vdev) < 0) { + return -1; + } + return 0; +} + +static void virtio_scsi_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + k->init = virtio_scsi_pci_init_pci; + dc->props = virtio_scsi_pci_properties; + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI; + pcidev_k->revision = 0x00; + pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI; +} + +static void virtio_scsi_pci_instance_init(Object *obj) +{ + VirtIOSCSIPCI *dev = VIRTIO_SCSI_PCI(obj); + object_initialize(OBJECT(&dev->vdev), TYPE_VIRTIO_SCSI); + object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL); +} + +static const TypeInfo virtio_scsi_pci_info = { + .name = TYPE_VIRTIO_SCSI_PCI, + .parent = TYPE_VIRTIO_PCI, + .instance_size = sizeof(VirtIOSCSIPCI), + .instance_init = virtio_scsi_pci_instance_init, + .class_init = virtio_scsi_pci_class_init, +}; + /* virtio-pci-bus */ void virtio_pci_bus_new(VirtioBusState *bus, VirtIOPCIProxy *dev) @@ -1493,7 +1469,7 @@ void virtio_pci_bus_new(VirtioBusState *bus, VirtIOPCIProxy *dev) BusState *qbus; qbus_create_inplace((BusState *)bus, TYPE_VIRTIO_PCI_BUS, qdev, NULL); qbus = BUS(bus); - qbus->allow_hotplug = 0; + qbus->allow_hotplug = 1; } static void virtio_pci_bus_class_init(ObjectClass *klass, void *data) @@ -1512,7 +1488,6 @@ static void virtio_pci_bus_class_init(ObjectClass *klass, void *data) k->set_guest_notifiers = virtio_pci_set_guest_notifiers; k->vmstate_change = virtio_pci_vmstate_change; k->device_plugged = virtio_pci_device_plugged; - k->device_unplug = virtio_pci_device_unplug; } static const TypeInfo virtio_pci_bus_info = { @@ -1524,17 +1499,17 @@ static const TypeInfo virtio_pci_bus_info = { static void virtio_pci_register_types(void) { - type_register_static(&virtio_blk_info); type_register_static(&virtio_net_info); type_register_static(&virtio_serial_info); type_register_static(&virtio_balloon_info); - type_register_static(&virtio_scsi_info); type_register_static(&virtio_rng_info); type_register_static(&virtio_pci_bus_info); type_register_static(&virtio_pci_info); #ifdef CONFIG_VIRTFS type_register_static(&virtio_9p_info); #endif + type_register_static(&virtio_blk_pci_info); + type_register_static(&virtio_scsi_pci_info); } type_init(virtio_pci_register_types) diff --git a/hw/virtio-pci.h b/hw/virtio-pci.h index 2ae96f84d6..bfe7a8e1ca 100644 --- a/hw/virtio-pci.h +++ b/hw/virtio-pci.h @@ -25,6 +25,8 @@ #include "hw/9pfs/virtio-9p-device.h" typedef struct VirtIOPCIProxy VirtIOPCIProxy; +typedef struct VirtIOBlkPCI VirtIOBlkPCI; +typedef struct VirtIOSCSIPCI VirtIOSCSIPCI; /* virtio-pci-bus */ @@ -73,7 +75,6 @@ struct VirtIOPCIProxy { uint32_t flags; uint32_t class_code; uint32_t nvectors; - VirtIOBlkConf blk; NICConf nic; uint32_t host_features; #ifdef CONFIG_VIRTFS @@ -81,7 +82,6 @@ struct VirtIOPCIProxy { #endif virtio_serial_conf serial; virtio_net_conf net; - VirtIOSCSIConf scsi; VirtIORNGConf rng; bool ioeventfd_disabled; bool ioeventfd_started; @@ -90,6 +90,32 @@ struct VirtIOPCIProxy { VirtioBusState bus; }; + +/* + * virtio-scsi-pci: This extends VirtioPCIProxy. + */ +#define TYPE_VIRTIO_SCSI_PCI "virtio-scsi-pci" +#define VIRTIO_SCSI_PCI(obj) \ + OBJECT_CHECK(VirtIOSCSIPCI, (obj), TYPE_VIRTIO_SCSI_PCI) + +struct VirtIOSCSIPCI { + VirtIOPCIProxy parent_obj; + VirtIOSCSI vdev; +}; + +/* + * virtio-blk-pci: This extends VirtioPCIProxy. + */ +#define TYPE_VIRTIO_BLK_PCI "virtio-blk-pci" +#define VIRTIO_BLK_PCI(obj) \ + OBJECT_CHECK(VirtIOBlkPCI, (obj), TYPE_VIRTIO_BLK_PCI) + +struct VirtIOBlkPCI { + VirtIOPCIProxy parent_obj; + VirtIOBlock vdev; + VirtIOBlkConf blk; +}; + void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev); void virtio_pci_bus_new(VirtioBusState *bus, VirtIOPCIProxy *dev); diff --git a/hw/virtio-rng.c b/hw/virtio-rng.c index 54c1421f86..fa8e8f3f9a 100644 --- a/hw/virtio-rng.c +++ b/hw/virtio-rng.c @@ -16,25 +16,6 @@ #include "hw/virtio-rng.h" #include "qemu/rng.h" -typedef struct VirtIORNG { - VirtIODevice vdev; - - DeviceState *qdev; - - /* Only one vq - guest puts buffer(s) on it when it needs entropy */ - VirtQueue *vq; - - VirtIORNGConf *conf; - - RngBackend *rng; - - /* We purposefully don't migrate this state. The quota will reset on the - * destination as a result. Rate limiting is host state, not guest state. - */ - QEMUTimer *rate_limit_timer; - int64_t quota_remaining; -} VirtIORNG; - static bool is_guest_ready(VirtIORNG *vrng) { if (virtio_queue_ready(vrng->vq) diff --git a/hw/virtio-rng.h b/hw/virtio-rng.h index f42d748eba..3711c97a70 100644 --- a/hw/virtio-rng.h +++ b/hw/virtio-rng.h @@ -25,4 +25,23 @@ struct VirtIORNGConf { RndRandom *default_backend; }; +typedef struct VirtIORNG { + VirtIODevice vdev; + + DeviceState *qdev; + + /* Only one vq - guest puts buffer(s) on it when it needs entropy */ + VirtQueue *vq; + + VirtIORNGConf *conf; + + RngBackend *rng; + + /* We purposefully don't migrate this state. The quota will reset on the + * destination as a result. Rate limiting is host state, not guest state. + */ + QEMUTimer *rate_limit_timer; + int64_t quota_remaining; +} VirtIORNG; + #endif diff --git a/hw/virtio-scsi.c b/hw/virtio-scsi.c index 72cc5198d4..06a58a6a63 100644 --- a/hw/virtio-scsi.c +++ b/hw/virtio-scsi.c @@ -17,6 +17,7 @@ #include "qemu/error-report.h" #include <hw/scsi.h> #include <hw/scsi-defs.h> +#include "hw/virtio-bus.h" #define VIRTIO_SCSI_VQ_SIZE 128 #define VIRTIO_SCSI_CDB_SIZE 32 @@ -130,21 +131,6 @@ typedef struct { uint32_t max_lun; } QEMU_PACKED VirtIOSCSIConfig; -typedef struct { - VirtIODevice vdev; - DeviceState *qdev; - VirtIOSCSIConf *conf; - - SCSIBus bus; - uint32_t sense_size; - uint32_t cdb_size; - int resetting; - bool events_dropped; - VirtQueue *ctrl_vq; - VirtQueue *event_vq; - VirtQueue *cmd_vqs[0]; -} VirtIOSCSI; - typedef struct VirtIOSCSIReq { VirtIOSCSI *dev; VirtQueue *vq; @@ -186,6 +172,7 @@ static void virtio_scsi_complete_req(VirtIOSCSIReq *req) { VirtIOSCSI *s = req->dev; VirtQueue *vq = req->vq; + VirtIODevice *vdev = VIRTIO_DEVICE(s); virtqueue_push(vq, &req->elem, req->qsgl.size + req->elem.in_sg[0].iov_len); qemu_sglist_destroy(&req->qsgl); if (req->sreq) { @@ -193,7 +180,7 @@ static void virtio_scsi_complete_req(VirtIOSCSIReq *req) scsi_req_unref(req->sreq); } g_free(req); - virtio_notify(&s->vdev, vq); + virtio_notify(vdev, vq); } static void virtio_scsi_bad_req(void) @@ -252,7 +239,7 @@ static void virtio_scsi_save_request(QEMUFile *f, SCSIRequest *sreq) VirtIOSCSIReq *req = sreq->hba_private; uint32_t n = virtio_queue_get_id(req->vq) - 2; - assert(n < req->dev->conf->num_queues); + assert(n < req->dev->conf.num_queues); qemu_put_be32s(f, &n); qemu_put_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem)); } @@ -266,7 +253,7 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq) req = g_malloc(sizeof(*req)); qemu_get_be32s(f, &n); - assert(n < s->conf->num_queues); + assert(n < s->conf.num_queues); qemu_get_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem)); virtio_scsi_parse_req(s, s->cmd_vqs[n], req); @@ -528,10 +515,10 @@ static void virtio_scsi_get_config(VirtIODevice *vdev, VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config; VirtIOSCSI *s = (VirtIOSCSI *)vdev; - stl_raw(&scsiconf->num_queues, s->conf->num_queues); + stl_raw(&scsiconf->num_queues, s->conf.num_queues); stl_raw(&scsiconf->seg_max, 128 - 2); - stl_raw(&scsiconf->max_sectors, s->conf->max_sectors); - stl_raw(&scsiconf->cmd_per_lun, s->conf->cmd_per_lun); + stl_raw(&scsiconf->max_sectors, s->conf.max_sectors); + stl_raw(&scsiconf->cmd_per_lun, s->conf.cmd_per_lun); stl_raw(&scsiconf->event_info_size, sizeof(VirtIOSCSIEvent)); stl_raw(&scsiconf->sense_size, s->sense_size); stl_raw(&scsiconf->cdb_size, s->cdb_size); @@ -580,16 +567,16 @@ static void virtio_scsi_reset(VirtIODevice *vdev) */ static void virtio_scsi_save(QEMUFile *f, void *opaque) { - VirtIOSCSI *s = opaque; - virtio_save(&s->vdev, f); + VirtIODevice *vdev = VIRTIO_DEVICE(opaque); + virtio_save(vdev, f); } static int virtio_scsi_load(QEMUFile *f, void *opaque, int version_id) { - VirtIOSCSI *s = opaque; + VirtIODevice *vdev = VIRTIO_DEVICE(opaque); int ret; - ret = virtio_load(&s->vdev, f); + ret = virtio_load(vdev, f); if (ret) { return ret; } @@ -601,9 +588,10 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, { VirtIOSCSIReq *req = virtio_scsi_pop_req(s, s->event_vq); VirtIOSCSIEvent *evt; + VirtIODevice *vdev = VIRTIO_DEVICE(s); int in_size; - if (!(s->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) { + if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { return; } @@ -647,7 +635,7 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, static void virtio_scsi_handle_event(VirtIODevice *vdev, VirtQueue *vq) { - VirtIOSCSI *s = (VirtIOSCSI *)vdev; + VirtIOSCSI *s = VIRTIO_SCSI(vdev); if (s->events_dropped) { virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0); @@ -657,8 +645,9 @@ static void virtio_scsi_handle_event(VirtIODevice *vdev, VirtQueue *vq) static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense) { VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); + VirtIODevice *vdev = VIRTIO_DEVICE(s); - if (((s->vdev.guest_features >> VIRTIO_SCSI_F_CHANGE) & 1) && + if (((vdev->guest_features >> VIRTIO_SCSI_F_CHANGE) & 1) && dev->type != TYPE_ROM) { virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_PARAM_CHANGE, sense.asc | (sense.ascq << 8)); @@ -668,8 +657,9 @@ static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense) static void virtio_scsi_hotplug(SCSIBus *bus, SCSIDevice *dev) { VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); + VirtIODevice *vdev = VIRTIO_DEVICE(s); - if ((s->vdev.guest_features >> VIRTIO_SCSI_F_HOTPLUG) & 1) { + if ((vdev->guest_features >> VIRTIO_SCSI_F_HOTPLUG) & 1) { virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_TRANSPORT_RESET, VIRTIO_SCSI_EVT_RESET_RESCAN); } @@ -678,8 +668,9 @@ static void virtio_scsi_hotplug(SCSIBus *bus, SCSIDevice *dev) static void virtio_scsi_hot_unplug(SCSIBus *bus, SCSIDevice *dev) { VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); + VirtIODevice *vdev = VIRTIO_DEVICE(s); - if ((s->vdev.guest_features >> VIRTIO_SCSI_F_HOTPLUG) & 1) { + if ((vdev->guest_features >> VIRTIO_SCSI_F_HOTPLUG) & 1) { virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_TRANSPORT_RESET, VIRTIO_SCSI_EVT_RESET_REMOVED); } @@ -701,49 +692,83 @@ static struct SCSIBusInfo virtio_scsi_scsi_info = { .load_request = virtio_scsi_load_request, }; -VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *proxyconf) +static int virtio_scsi_device_init(VirtIODevice *vdev) { - VirtIOSCSI *s; + DeviceState *qdev = DEVICE(vdev); + VirtIOSCSI *s = VIRTIO_SCSI(vdev); static int virtio_scsi_id; - size_t sz; int i; - sz = sizeof(VirtIOSCSI) + proxyconf->num_queues * sizeof(VirtQueue *); - s = (VirtIOSCSI *)virtio_common_init("virtio-scsi", VIRTIO_ID_SCSI, - sizeof(VirtIOSCSIConfig), sz); + virtio_init(VIRTIO_DEVICE(s), "virtio-scsi", VIRTIO_ID_SCSI, + sizeof(VirtIOSCSIConfig)); - s->qdev = dev; - s->conf = proxyconf; + s->cmd_vqs = g_malloc0(s->conf.num_queues * sizeof(VirtQueue *)); /* TODO set up vdev function pointers */ - s->vdev.get_config = virtio_scsi_get_config; - s->vdev.set_config = virtio_scsi_set_config; - s->vdev.get_features = virtio_scsi_get_features; - s->vdev.reset = virtio_scsi_reset; - - s->ctrl_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE, - virtio_scsi_handle_ctrl); - s->event_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE, + vdev->get_config = virtio_scsi_get_config; + vdev->set_config = virtio_scsi_set_config; + vdev->get_features = virtio_scsi_get_features; + vdev->reset = virtio_scsi_reset; + + s->ctrl_vq = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE, + virtio_scsi_handle_ctrl); + s->event_vq = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE, virtio_scsi_handle_event); - for (i = 0; i < s->conf->num_queues; i++) { - s->cmd_vqs[i] = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE, + for (i = 0; i < s->conf.num_queues; i++) { + s->cmd_vqs[i] = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE, virtio_scsi_handle_cmd); } - scsi_bus_new(&s->bus, dev, &virtio_scsi_scsi_info); - if (!dev->hotplugged) { + scsi_bus_new(&s->bus, qdev, &virtio_scsi_scsi_info); + if (!qdev->hotplugged) { scsi_bus_legacy_handle_cmdline(&s->bus); } - register_savevm(dev, "virtio-scsi", virtio_scsi_id++, 1, + register_savevm(qdev, "virtio-scsi", virtio_scsi_id++, 1, virtio_scsi_save, virtio_scsi_load, s); - return &s->vdev; + return 0; } -void virtio_scsi_exit(VirtIODevice *vdev) +static int virtio_scsi_device_exit(DeviceState *qdev) { - VirtIOSCSI *s = (VirtIOSCSI *)vdev; - unregister_savevm(s->qdev, "virtio-scsi", s); - virtio_cleanup(vdev); + VirtIOSCSI *s = VIRTIO_SCSI(qdev); + VirtIODevice *vdev = VIRTIO_DEVICE(qdev); + + unregister_savevm(qdev, "virtio-scsi", s); + g_free(s->cmd_vqs); + virtio_common_cleanup(vdev); + return 0; } + +static Property virtio_scsi_properties[] = { + DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOSCSI, conf), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_scsi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + dc->exit = virtio_scsi_device_exit; + dc->props = virtio_scsi_properties; + vdc->init = virtio_scsi_device_init; + vdc->get_config = virtio_scsi_get_config; + vdc->set_config = virtio_scsi_set_config; + vdc->get_features = virtio_scsi_get_features; + vdc->reset = virtio_scsi_reset; +} + +static const TypeInfo virtio_scsi_info = { + .name = TYPE_VIRTIO_SCSI, + .parent = TYPE_VIRTIO_DEVICE, + .instance_size = sizeof(VirtIOSCSI), + .class_init = virtio_scsi_class_init, +}; + +static void virtio_register_types(void) +{ + type_register_static(&virtio_scsi_info); +} + +type_init(virtio_register_types) diff --git a/hw/virtio-scsi.h b/hw/virtio-scsi.h index 81b3279a57..31e97bbe38 100644 --- a/hw/virtio-scsi.h +++ b/hw/virtio-scsi.h @@ -16,6 +16,12 @@ #include "hw/virtio.h" #include "hw/pci/pci.h" +#include "hw/scsi.h" + +#define TYPE_VIRTIO_SCSI "virtio-scsi" +#define VIRTIO_SCSI(obj) \ + OBJECT_CHECK(VirtIOSCSI, (obj), TYPE_VIRTIO_SCSI) + /* The ID for virtio_scsi */ #define VIRTIO_ID_SCSI 8 @@ -31,12 +37,30 @@ struct VirtIOSCSIConf { uint32_t cmd_per_lun; }; -#define DEFINE_VIRTIO_SCSI_PROPERTIES(_state, _features_field, _conf_field) \ - DEFINE_VIRTIO_COMMON_FEATURES(_state, _features_field), \ - DEFINE_PROP_UINT32("num_queues", _state, _conf_field.num_queues, 1), \ - DEFINE_PROP_UINT32("max_sectors", _state, _conf_field.max_sectors, 0xFFFF), \ - DEFINE_PROP_UINT32("cmd_per_lun", _state, _conf_field.cmd_per_lun, 128), \ - DEFINE_PROP_BIT("hotplug", _state, _features_field, VIRTIO_SCSI_F_HOTPLUG, true), \ - DEFINE_PROP_BIT("param_change", _state, _features_field, VIRTIO_SCSI_F_CHANGE, true) +typedef struct VirtIOSCSI { + VirtIODevice parent_obj; + VirtIOSCSIConf conf; + + SCSIBus bus; + uint32_t sense_size; + uint32_t cdb_size; + int resetting; + bool events_dropped; + VirtQueue *ctrl_vq; + VirtQueue *event_vq; + VirtQueue **cmd_vqs; +} VirtIOSCSI; + +#define DEFINE_VIRTIO_SCSI_PROPERTIES(_state, _conf_field) \ + DEFINE_PROP_UINT32("num_queues", _state, _conf_field.num_queues, 1), \ + DEFINE_PROP_UINT32("max_sectors", _state, _conf_field.max_sectors, 0xFFFF),\ + DEFINE_PROP_UINT32("cmd_per_lun", _state, _conf_field.cmd_per_lun, 128) + +#define DEFINE_VIRTIO_SCSI_FEATURES(_state, _feature_field) \ + DEFINE_VIRTIO_COMMON_FEATURES(_state, _feature_field), \ + DEFINE_PROP_BIT("hotplug", _state, _feature_field, VIRTIO_SCSI_F_HOTPLUG, \ + true), \ + DEFINE_PROP_BIT("param_change", _state, _feature_field, \ + VIRTIO_SCSI_F_CHANGE, true) #endif /* _QEMU_VIRTIO_SCSI_H */ diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c index 7d0515f551..ab7168ed32 100644 --- a/hw/virtio-serial-bus.c +++ b/hw/virtio-serial-bus.c @@ -25,47 +25,6 @@ #include "trace.h" #include "hw/virtio-serial.h" -/* The virtio-serial bus on top of which the ports will ride as devices */ -struct VirtIOSerialBus { - BusState qbus; - - /* This is the parent device that provides the bus for ports. */ - VirtIOSerial *vser; - - /* The maximum number of ports that can ride on top of this bus */ - uint32_t max_nr_ports; -}; - -typedef struct VirtIOSerialPostLoad { - QEMUTimer *timer; - uint32_t nr_active_ports; - struct { - VirtIOSerialPort *port; - uint8_t host_connected; - } *connected; -} VirtIOSerialPostLoad; - -struct VirtIOSerial { - VirtIODevice vdev; - - VirtQueue *c_ivq, *c_ovq; - /* Arrays of ivqs and ovqs: one per port */ - VirtQueue **ivqs, **ovqs; - - VirtIOSerialBus bus; - - DeviceState *qdev; - - QTAILQ_HEAD(, VirtIOSerialPort) ports; - - /* bitmap for identifying active ports */ - uint32_t *ports_map; - - struct virtio_console_config config; - - struct VirtIOSerialPostLoad *post_load; -}; - static VirtIOSerialPort *find_port_by_id(VirtIOSerial *vser, uint32_t id) { VirtIOSerialPort *port; diff --git a/hw/virtio-serial.h b/hw/virtio-serial.h index d2d9fb773e..484dcfef2c 100644 --- a/hw/virtio-serial.h +++ b/hw/virtio-serial.h @@ -173,6 +173,47 @@ struct VirtIOSerialPort { bool throttled; }; +/* The virtio-serial bus on top of which the ports will ride as devices */ +struct VirtIOSerialBus { + BusState qbus; + + /* This is the parent device that provides the bus for ports. */ + VirtIOSerial *vser; + + /* The maximum number of ports that can ride on top of this bus */ + uint32_t max_nr_ports; +}; + +typedef struct VirtIOSerialPostLoad { + QEMUTimer *timer; + uint32_t nr_active_ports; + struct { + VirtIOSerialPort *port; + uint8_t host_connected; + } *connected; +} VirtIOSerialPostLoad; + +struct VirtIOSerial { + VirtIODevice vdev; + + VirtQueue *c_ivq, *c_ovq; + /* Arrays of ivqs and ovqs: one per port */ + VirtQueue **ivqs, **ovqs; + + VirtIOSerialBus bus; + + DeviceState *qdev; + + QTAILQ_HEAD(, VirtIOSerialPort) ports; + + /* bitmap for identifying active ports */ + uint32_t *ports_map; + + struct virtio_console_config config; + + struct VirtIOSerialPostLoad *post_load; +}; + /* Interface to the virtio-serial bus */ /* diff --git a/hw/virtio.h b/hw/virtio.h index ca43fd70cd..fdbe9313a0 100644 --- a/hw/virtio.h +++ b/hw/virtio.h @@ -240,7 +240,6 @@ void virtio_bind_device(VirtIODevice *vdev, const VirtIOBindings *binding, /* Base devices. */ typedef struct VirtIOBlkConf VirtIOBlkConf; -VirtIODevice *virtio_blk_init(DeviceState *dev, VirtIOBlkConf *blk); struct virtio_net_conf; VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, struct virtio_net_conf *net, @@ -258,7 +257,6 @@ VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf); void virtio_net_exit(VirtIODevice *vdev); -void virtio_blk_exit(VirtIODevice *vdev); void virtio_serial_exit(VirtIODevice *vdev); void virtio_balloon_exit(VirtIODevice *vdev); void virtio_scsi_exit(VirtIODevice *vdev); diff --git a/hw/vmware_utils.h b/hw/vmware_utils.h new file mode 100644 index 0000000000..5307e2ccc9 --- /dev/null +++ b/hw/vmware_utils.h @@ -0,0 +1,143 @@ +/* + * QEMU VMWARE paravirtual devices - auxiliary code + * + * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) + * + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Dmitry Fleytman <dmitry@daynix.com> + * Yan Vugenfirer <yan@daynix.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef VMWARE_UTILS_H +#define VMWARE_UTILS_H + +#include "qemu/range.h" + +#ifndef VMW_SHPRN +#define VMW_SHPRN(fmt, ...) do {} while (0) +#endif + +/* + * Shared memory access functions with byte swap support + * Each function contains printout for reverse-engineering needs + * + */ +static inline void +vmw_shmem_read(hwaddr addr, void *buf, int len) +{ + VMW_SHPRN("SHMEM r: %" PRIx64 ", len: %d to %p", addr, len, buf); + cpu_physical_memory_read(addr, buf, len); +} + +static inline void +vmw_shmem_write(hwaddr addr, void *buf, int len) +{ + VMW_SHPRN("SHMEM w: %" PRIx64 ", len: %d to %p", addr, len, buf); + cpu_physical_memory_write(addr, buf, len); +} + +static inline void +vmw_shmem_rw(hwaddr addr, void *buf, int len, int is_write) +{ + VMW_SHPRN("SHMEM r/w: %" PRIx64 ", len: %d (to %p), is write: %d", + addr, len, buf, is_write); + + cpu_physical_memory_rw(addr, buf, len, is_write); +} + +static inline void +vmw_shmem_set(hwaddr addr, uint8 val, int len) +{ + int i; + VMW_SHPRN("SHMEM set: %" PRIx64 ", len: %d (value 0x%X)", addr, len, val); + + for (i = 0; i < len; i++) { + cpu_physical_memory_write(addr + i, &val, 1); + } +} + +static inline uint32_t +vmw_shmem_ld8(hwaddr addr) +{ + uint8_t res = ldub_phys(addr); + VMW_SHPRN("SHMEM load8: %" PRIx64 " (value 0x%X)", addr, res); + return res; +} + +static inline void +vmw_shmem_st8(hwaddr addr, uint8_t value) +{ + VMW_SHPRN("SHMEM store8: %" PRIx64 " (value 0x%X)", addr, value); + stb_phys(addr, value); +} + +static inline uint32_t +vmw_shmem_ld16(hwaddr addr) +{ + uint16_t res = lduw_le_phys(addr); + VMW_SHPRN("SHMEM load16: %" PRIx64 " (value 0x%X)", addr, res); + return res; +} + +static inline void +vmw_shmem_st16(hwaddr addr, uint16_t value) +{ + VMW_SHPRN("SHMEM store16: %" PRIx64 " (value 0x%X)", addr, value); + stw_le_phys(addr, value); +} + +static inline uint32_t +vmw_shmem_ld32(hwaddr addr) +{ + uint32_t res = ldl_le_phys(addr); + VMW_SHPRN("SHMEM load32: %" PRIx64 " (value 0x%X)", addr, res); + return res; +} + +static inline void +vmw_shmem_st32(hwaddr addr, uint32_t value) +{ + VMW_SHPRN("SHMEM store32: %" PRIx64 " (value 0x%X)", addr, value); + stl_le_phys(addr, value); +} + +static inline uint64_t +vmw_shmem_ld64(hwaddr addr) +{ + uint64_t res = ldq_le_phys(addr); + VMW_SHPRN("SHMEM load64: %" PRIx64 " (value %" PRIx64 ")", addr, res); + return res; +} + +static inline void +vmw_shmem_st64(hwaddr addr, uint64_t value) +{ + VMW_SHPRN("SHMEM store64: %" PRIx64 " (value %" PRIx64 ")", addr, value); + stq_le_phys(addr, value); +} + +/* Macros for simplification of operations on array-style registers */ + +/* + * Whether <addr> lies inside of array-style register defined by <base>, + * number of elements (<cnt>) and element size (<regsize>) + * +*/ +#define VMW_IS_MULTIREG_ADDR(addr, base, cnt, regsize) \ + range_covers_byte(base, cnt * regsize, addr) + +/* + * Returns index of given register (<addr>) in array-style register defined by + * <base> and element size (<regsize>) + * +*/ +#define VMW_MULTIREG_IDX_BY_ADDR(addr, base, regsize) \ + (((addr) - (base)) / (regsize)) + +#endif diff --git a/hw/vmware_vga.c b/hw/vmware_vga.c index db2f187e56..5b9ce8f96b 100644 --- a/hw/vmware_vga.c +++ b/hw/vmware_vga.c @@ -57,9 +57,6 @@ struct vmsvga_state_s { int new_height; uint32_t guest; uint32_t svgaid; - uint32_t wred; - uint32_t wgreen; - uint32_t wblue; int syncing; MemoryRegion fifo_ram; @@ -289,6 +286,7 @@ enum { static inline void vmsvga_update_rect(struct vmsvga_state_s *s, int x, int y, int w, int h) { + DisplaySurface *surface = qemu_console_surface(s->vga.con); int line; int bypl; int width; @@ -305,11 +303,11 @@ static inline void vmsvga_update_rect(struct vmsvga_state_s *s, fprintf(stderr, "%s: update w was < 0 (%d)\n", __func__, w); w = 0; } - if (x + w > ds_get_width(s->vga.ds)) { + if (x + w > surface_width(surface)) { fprintf(stderr, "%s: update width too large x: %d, w: %d\n", __func__, x, w); - x = MIN(x, ds_get_width(s->vga.ds)); - w = ds_get_width(s->vga.ds) - x; + x = MIN(x, surface_width(surface)); + w = surface_width(surface) - x; } if (y < 0) { @@ -321,23 +319,23 @@ static inline void vmsvga_update_rect(struct vmsvga_state_s *s, fprintf(stderr, "%s: update h was < 0 (%d)\n", __func__, h); h = 0; } - if (y + h > ds_get_height(s->vga.ds)) { + if (y + h > surface_height(surface)) { fprintf(stderr, "%s: update height too large y: %d, h: %d\n", __func__, y, h); - y = MIN(y, ds_get_height(s->vga.ds)); - h = ds_get_height(s->vga.ds) - y; + y = MIN(y, surface_height(surface)); + h = surface_height(surface) - y; } - bypl = ds_get_linesize(s->vga.ds); - width = ds_get_bytes_per_pixel(s->vga.ds) * w; - start = ds_get_bytes_per_pixel(s->vga.ds) * x + bypl * y; + bypl = surface_stride(surface); + width = surface_bytes_per_pixel(surface) * w; + start = surface_bytes_per_pixel(surface) * x + bypl * y; src = s->vga.vram_ptr + start; - dst = ds_get_data(s->vga.ds) + start; + dst = surface_data(surface) + start; for (line = h; line > 0; line--, src += bypl, dst += bypl) { memcpy(dst, src, width); } - dpy_gfx_update(s->vga.ds, x, y, w, h); + dpy_gfx_update(s->vga.con, x, y, w, h); } static inline void vmsvga_update_rect_delayed(struct vmsvga_state_s *s, @@ -373,9 +371,10 @@ static inline void vmsvga_update_rect_flush(struct vmsvga_state_s *s) static inline void vmsvga_copy_rect(struct vmsvga_state_s *s, int x0, int y0, int x1, int y1, int w, int h) { + DisplaySurface *surface = qemu_console_surface(s->vga.con); uint8_t *vram = s->vga.vram_ptr; - int bypl = ds_get_linesize(s->vga.ds); - int bypp = ds_get_bytes_per_pixel(s->vga.ds); + int bypl = surface_stride(surface); + int bypp = surface_bytes_per_pixel(surface); int width = bypp * w; int line = h; uint8_t *ptr[2]; @@ -402,8 +401,9 @@ static inline void vmsvga_copy_rect(struct vmsvga_state_s *s, static inline void vmsvga_fill_rect(struct vmsvga_state_s *s, uint32_t c, int x, int y, int w, int h) { - int bypl = ds_get_linesize(s->vga.ds); - int width = ds_get_bytes_per_pixel(s->vga.ds) * w; + DisplaySurface *surface = qemu_console_surface(s->vga.con); + int bypl = surface_stride(surface); + int width = surface_bytes_per_pixel(surface) * w; int line = h; int column; uint8_t *fst; @@ -416,14 +416,14 @@ static inline void vmsvga_fill_rect(struct vmsvga_state_s *s, col[2] = c >> 16; col[3] = c >> 24; - fst = s->vga.vram_ptr + ds_get_bytes_per_pixel(s->vga.ds) * x + bypl * y; + fst = s->vga.vram_ptr + surface_bytes_per_pixel(surface) * x + bypl * y; if (line--) { dst = fst; src = col; for (column = width; column > 0; column--) { *(dst++) = *(src++); - if (src - col == ds_get_bytes_per_pixel(s->vga.ds)) { + if (src - col == surface_bytes_per_pixel(surface)) { src = col; } } @@ -490,7 +490,7 @@ static inline void vmsvga_cursor_define(struct vmsvga_state_s *s, qc = cursor_builtin_left_ptr(); } - dpy_cursor_define(s->vga.ds, qc); + dpy_cursor_define(s->vga.con, qc); cursor_put(qc); } #endif @@ -720,6 +720,7 @@ static uint32_t vmsvga_value_read(void *opaque, uint32_t address) { uint32_t caps; struct vmsvga_state_s *s = opaque; + DisplaySurface *surface = qemu_console_surface(s->vga.con); switch (s->index) { case SVGA_REG_ID: @@ -729,10 +730,10 @@ static uint32_t vmsvga_value_read(void *opaque, uint32_t address) return s->enable; case SVGA_REG_WIDTH: - return ds_get_width(s->vga.ds); + return surface_width(surface); case SVGA_REG_HEIGHT: - return ds_get_height(s->vga.ds); + return surface_height(surface); case SVGA_REG_MAX_WIDTH: return SVGA_MAX_WIDTH; @@ -750,13 +751,13 @@ static uint32_t vmsvga_value_read(void *opaque, uint32_t address) return 0x0; case SVGA_REG_RED_MASK: - return s->wred; + return surface->pf.rmask; case SVGA_REG_GREEN_MASK: - return s->wgreen; + return surface->pf.gmask; case SVGA_REG_BLUE_MASK: - return s->wblue; + return surface->pf.bmask; case SVGA_REG_BYTES_PER_LINE: return s->bypp * s->new_width; @@ -785,7 +786,7 @@ static uint32_t vmsvga_value_read(void *opaque, uint32_t address) caps |= SVGA_CAP_RECT_FILL; #endif #ifdef HW_MOUSE_ACCEL - if (dpy_cursor_define_supported(s->vga.ds)) { + if (dpy_cursor_define_supported(s->vga.con)) { caps |= SVGA_CAP_CURSOR | SVGA_CAP_CURSOR_BYPASS_2 | SVGA_CAP_CURSOR_BYPASS; } @@ -947,7 +948,7 @@ static void vmsvga_value_write(void *opaque, uint32_t address, uint32_t value) s->cursor.on &= (value != SVGA_CURSOR_ON_HIDE); #ifdef HW_MOUSE_ACCEL if (value <= SVGA_CURSOR_ON_SHOW) { - dpy_mouse_set(s->vga.ds, s->cursor.x, s->cursor.y, s->cursor.on); + dpy_mouse_set(s->vga.con, s->cursor.x, s->cursor.y, s->cursor.on); } #endif break; @@ -982,9 +983,11 @@ static void vmsvga_bios_write(void *opaque, uint32_t address, uint32_t data) static inline void vmsvga_check_size(struct vmsvga_state_s *s) { - if (s->new_width != ds_get_width(s->vga.ds) || - s->new_height != ds_get_height(s->vga.ds)) { - qemu_console_resize(s->vga.ds, s->new_width, s->new_height); + DisplaySurface *surface = qemu_console_surface(s->vga.con); + + if (s->new_width != surface_width(surface) || + s->new_height != surface_height(surface)) { + qemu_console_resize(s->vga.con, s->new_width, s->new_height); s->invalidated = 1; } } @@ -992,6 +995,7 @@ static inline void vmsvga_check_size(struct vmsvga_state_s *s) static void vmsvga_update_display(void *opaque) { struct vmsvga_state_s *s = opaque; + DisplaySurface *surface = qemu_console_surface(s->vga.con); bool dirty = false; if (!s->enable) { @@ -1011,19 +1015,19 @@ static void vmsvga_update_display(void *opaque) if (memory_region_is_logging(&s->vga.vram)) { vga_sync_dirty_bitmap(&s->vga); dirty = memory_region_get_dirty(&s->vga.vram, 0, - ds_get_linesize(s->vga.ds) * ds_get_height(s->vga.ds), + surface_stride(surface) * surface_height(surface), DIRTY_MEMORY_VGA); } if (s->invalidated || dirty) { s->invalidated = 0; - memcpy(ds_get_data(s->vga.ds), s->vga.vram_ptr, - ds_get_linesize(s->vga.ds) * ds_get_height(s->vga.ds)); - dpy_gfx_update(s->vga.ds, 0, 0, - ds_get_width(s->vga.ds), ds_get_height(s->vga.ds)); + memcpy(surface_data(surface), s->vga.vram_ptr, + surface_stride(surface) * surface_height(surface)); + dpy_gfx_update(s->vga.con, 0, 0, + surface_width(surface), surface_height(surface)); } if (dirty) { memory_region_reset_dirty(&s->vga.vram, 0, - ds_get_linesize(s->vga.ds) * ds_get_height(s->vga.ds), + surface_stride(surface) * surface_height(surface), DIRTY_MEMORY_VGA); } } @@ -1063,17 +1067,19 @@ static void vmsvga_screen_dump(void *opaque, const char *filename, bool cswitch, Error **errp) { struct vmsvga_state_s *s = opaque; + DisplaySurface *surface = qemu_console_surface(s->vga.con); + if (!s->enable) { s->vga.screen_dump(&s->vga, filename, cswitch, errp); return; } - if (ds_get_bits_per_pixel(s->vga.ds) == 32) { + if (surface_bits_per_pixel(surface) == 32) { DisplaySurface *ds = qemu_create_displaysurface_from( - ds_get_width(s->vga.ds), - ds_get_height(s->vga.ds), + surface_width(surface), + surface_height(surface), 32, - ds_get_linesize(s->vga.ds), + surface_stride(surface), s->vga.vram_ptr, false); ppm_save(filename, ds, errp); g_free(ds); @@ -1143,14 +1149,16 @@ static const VMStateDescription vmstate_vmware_vga = { static void vmsvga_init(struct vmsvga_state_s *s, MemoryRegion *address_space, MemoryRegion *io) { + DisplaySurface *surface; + s->scratch_size = SVGA_SCRATCH_SIZE; s->scratch = g_malloc(s->scratch_size * 4); - s->vga.ds = graphic_console_init(vmsvga_update_display, - vmsvga_invalidate_display, - vmsvga_screen_dump, - vmsvga_text_update, s); - + s->vga.con = graphic_console_init(vmsvga_update_display, + vmsvga_invalidate_display, + vmsvga_screen_dump, + vmsvga_text_update, s); + surface = qemu_console_surface(s->vga.con); s->fifo_size = SVGA_FIFO_SIZE; memory_region_init_ram(&s->fifo_ram, "vmsvga.fifo", s->fifo_size); @@ -1162,11 +1170,8 @@ static void vmsvga_init(struct vmsvga_state_s *s, vmstate_register(NULL, 0, &vmstate_vga_common, &s->vga); /* Save some values here in case they are changed later. * This is suspicious and needs more though why it is needed. */ - s->depth = ds_get_bits_per_pixel(s->vga.ds); - s->bypp = ds_get_bytes_per_pixel(s->vga.ds); - s->wred = ds_get_rmask(s->vga.ds); - s->wgreen = ds_get_gmask(s->vga.ds); - s->wblue = ds_get_bmask(s->vga.ds); + s->depth = surface_bits_per_pixel(surface); + s->bypp = surface_bytes_per_pixel(surface); } static uint64_t vmsvga_io_read(void *opaque, hwaddr addr, unsigned size) diff --git a/hw/vmxnet3.c b/hw/vmxnet3.c new file mode 100644 index 0000000000..925be8084a --- /dev/null +++ b/hw/vmxnet3.c @@ -0,0 +1,2461 @@ +/* + * QEMU VMWARE VMXNET3 paravirtual NIC + * + * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) + * + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Dmitry Fleytman <dmitry@daynix.com> + * Tamir Shomer <tamirs@daynix.com> + * Yan Vugenfirer <yan@daynix.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + * + */ + +#include "hw.h" +#include "pci/pci.h" +#include "net/net.h" +#include "virtio-net.h" +#include "net/tap.h" +#include "net/checksum.h" +#include "sysemu/sysemu.h" +#include "qemu-common.h" +#include "qemu/bswap.h" +#include "pci/msix.h" +#include "pci/msi.h" + +#include "vmxnet3.h" +#include "vmxnet_debug.h" +#include "vmware_utils.h" +#include "vmxnet_tx_pkt.h" +#include "vmxnet_rx_pkt.h" + +#define PCI_DEVICE_ID_VMWARE_VMXNET3_REVISION 0x1 +#define VMXNET3_MSIX_BAR_SIZE 0x2000 + +#define VMXNET3_BAR0_IDX (0) +#define VMXNET3_BAR1_IDX (1) +#define VMXNET3_MSIX_BAR_IDX (2) + +#define VMXNET3_OFF_MSIX_TABLE (0x000) +#define VMXNET3_OFF_MSIX_PBA (0x800) + +/* Link speed in Mbps should be shifted by 16 */ +#define VMXNET3_LINK_SPEED (1000 << 16) + +/* Link status: 1 - up, 0 - down. */ +#define VMXNET3_LINK_STATUS_UP 0x1 + +/* Least significant bit should be set for revision and version */ +#define VMXNET3_DEVICE_VERSION 0x1 +#define VMXNET3_DEVICE_REVISION 0x1 + +/* Macros for rings descriptors access */ +#define VMXNET3_READ_TX_QUEUE_DESCR8(dpa, field) \ + (vmw_shmem_ld8(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field))) + +#define VMXNET3_WRITE_TX_QUEUE_DESCR8(dpa, field, value) \ + (vmw_shmem_st8(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field, value))) + +#define VMXNET3_READ_TX_QUEUE_DESCR32(dpa, field) \ + (vmw_shmem_ld32(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field))) + +#define VMXNET3_WRITE_TX_QUEUE_DESCR32(dpa, field, value) \ + (vmw_shmem_st32(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field), value)) + +#define VMXNET3_READ_TX_QUEUE_DESCR64(dpa, field) \ + (vmw_shmem_ld64(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field))) + +#define VMXNET3_WRITE_TX_QUEUE_DESCR64(dpa, field, value) \ + (vmw_shmem_st64(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field), value)) + +#define VMXNET3_READ_RX_QUEUE_DESCR64(dpa, field) \ + (vmw_shmem_ld64(dpa + offsetof(struct Vmxnet3_RxQueueDesc, field))) + +#define VMXNET3_READ_RX_QUEUE_DESCR32(dpa, field) \ + (vmw_shmem_ld32(dpa + offsetof(struct Vmxnet3_RxQueueDesc, field))) + +#define VMXNET3_WRITE_RX_QUEUE_DESCR64(dpa, field, value) \ + (vmw_shmem_st64(dpa + offsetof(struct Vmxnet3_RxQueueDesc, field), value)) + +#define VMXNET3_WRITE_RX_QUEUE_DESCR8(dpa, field, value) \ + (vmw_shmem_st8(dpa + offsetof(struct Vmxnet3_RxQueueDesc, field), value)) + +/* Macros for guest driver shared area access */ +#define VMXNET3_READ_DRV_SHARED64(shpa, field) \ + (vmw_shmem_ld64(shpa + offsetof(struct Vmxnet3_DriverShared, field))) + +#define VMXNET3_READ_DRV_SHARED32(shpa, field) \ + (vmw_shmem_ld32(shpa + offsetof(struct Vmxnet3_DriverShared, field))) + +#define VMXNET3_WRITE_DRV_SHARED32(shpa, field, val) \ + (vmw_shmem_st32(shpa + offsetof(struct Vmxnet3_DriverShared, field), val)) + +#define VMXNET3_READ_DRV_SHARED16(shpa, field) \ + (vmw_shmem_ld16(shpa + offsetof(struct Vmxnet3_DriverShared, field))) + +#define VMXNET3_READ_DRV_SHARED8(shpa, field) \ + (vmw_shmem_ld8(shpa + offsetof(struct Vmxnet3_DriverShared, field))) + +#define VMXNET3_READ_DRV_SHARED(shpa, field, b, l) \ + (vmw_shmem_read(shpa + offsetof(struct Vmxnet3_DriverShared, field), b, l)) + +#define VMXNET_FLAG_IS_SET(field, flag) (((field) & (flag)) == (flag)) + +#define TYPE_VMXNET3 "vmxnet3" +#define VMXNET3(obj) OBJECT_CHECK(VMXNET3State, (obj), TYPE_VMXNET3) + +/* Cyclic ring abstraction */ +typedef struct { + hwaddr pa; + size_t size; + size_t cell_size; + size_t next; + uint8_t gen; +} Vmxnet3Ring; + +static inline void vmxnet3_ring_init(Vmxnet3Ring *ring, + hwaddr pa, + size_t size, + size_t cell_size, + bool zero_region) +{ + ring->pa = pa; + ring->size = size; + ring->cell_size = cell_size; + ring->gen = VMXNET3_INIT_GEN; + ring->next = 0; + + if (zero_region) { + vmw_shmem_set(pa, 0, size * cell_size); + } +} + +#define VMXNET3_RING_DUMP(macro, ring_name, ridx, r) \ + macro("%s#%d: base %" PRIx64 " size %lu cell_size %lu gen %d next %lu", \ + (ring_name), (ridx), \ + (r)->pa, (r)->size, (r)->cell_size, (r)->gen, (r)->next) + +static inline void vmxnet3_ring_inc(Vmxnet3Ring *ring) +{ + if (++ring->next >= ring->size) { + ring->next = 0; + ring->gen ^= 1; + } +} + +static inline void vmxnet3_ring_dec(Vmxnet3Ring *ring) +{ + if (ring->next-- == 0) { + ring->next = ring->size - 1; + ring->gen ^= 1; + } +} + +static inline hwaddr vmxnet3_ring_curr_cell_pa(Vmxnet3Ring *ring) +{ + return ring->pa + ring->next * ring->cell_size; +} + +static inline void vmxnet3_ring_read_curr_cell(Vmxnet3Ring *ring, void *buff) +{ + vmw_shmem_read(vmxnet3_ring_curr_cell_pa(ring), buff, ring->cell_size); +} + +static inline void vmxnet3_ring_write_curr_cell(Vmxnet3Ring *ring, void *buff) +{ + vmw_shmem_write(vmxnet3_ring_curr_cell_pa(ring), buff, ring->cell_size); +} + +static inline size_t vmxnet3_ring_curr_cell_idx(Vmxnet3Ring *ring) +{ + return ring->next; +} + +static inline uint8_t vmxnet3_ring_curr_gen(Vmxnet3Ring *ring) +{ + return ring->gen; +} + +/* Debug trace-related functions */ +static inline void +vmxnet3_dump_tx_descr(struct Vmxnet3_TxDesc *descr) +{ + VMW_PKPRN("TX DESCR: " + "addr %" PRIx64 ", len: %d, gen: %d, rsvd: %d, " + "dtype: %d, ext1: %d, msscof: %d, hlen: %d, om: %d, " + "eop: %d, cq: %d, ext2: %d, ti: %d, tci: %d", + le64_to_cpu(descr->addr), descr->len, descr->gen, descr->rsvd, + descr->dtype, descr->ext1, descr->msscof, descr->hlen, descr->om, + descr->eop, descr->cq, descr->ext2, descr->ti, descr->tci); +} + +static inline void +vmxnet3_dump_virt_hdr(struct virtio_net_hdr *vhdr) +{ + VMW_PKPRN("VHDR: flags 0x%x, gso_type: 0x%x, hdr_len: %d, gso_size: %d, " + "csum_start: %d, csum_offset: %d", + vhdr->flags, vhdr->gso_type, vhdr->hdr_len, vhdr->gso_size, + vhdr->csum_start, vhdr->csum_offset); +} + +static inline void +vmxnet3_dump_rx_descr(struct Vmxnet3_RxDesc *descr) +{ + VMW_PKPRN("RX DESCR: addr %" PRIx64 ", len: %d, gen: %d, rsvd: %d, " + "dtype: %d, ext1: %d, btype: %d", + le64_to_cpu(descr->addr), descr->len, descr->gen, + descr->rsvd, descr->dtype, descr->ext1, descr->btype); +} + +/* Device state and helper functions */ +#define VMXNET3_RX_RINGS_PER_QUEUE (2) + +typedef struct { + Vmxnet3Ring tx_ring; + Vmxnet3Ring comp_ring; + + uint8_t intr_idx; + hwaddr tx_stats_pa; + struct UPT1_TxStats txq_stats; +} Vmxnet3TxqDescr; + +typedef struct { + Vmxnet3Ring rx_ring[VMXNET3_RX_RINGS_PER_QUEUE]; + Vmxnet3Ring comp_ring; + uint8_t intr_idx; + hwaddr rx_stats_pa; + struct UPT1_RxStats rxq_stats; +} Vmxnet3RxqDescr; + +typedef struct { + bool is_masked; + bool is_pending; + bool is_asserted; +} Vmxnet3IntState; + +typedef struct { + PCIDevice parent_obj; + NICState *nic; + NICConf conf; + MemoryRegion bar0; + MemoryRegion bar1; + MemoryRegion msix_bar; + + Vmxnet3RxqDescr rxq_descr[VMXNET3_DEVICE_MAX_RX_QUEUES]; + Vmxnet3TxqDescr txq_descr[VMXNET3_DEVICE_MAX_TX_QUEUES]; + + /* Whether MSI-X support was installed successfully */ + bool msix_used; + /* Whether MSI support was installed successfully */ + bool msi_used; + hwaddr drv_shmem; + hwaddr temp_shared_guest_driver_memory; + + uint8_t txq_num; + + /* This boolean tells whether RX packet being indicated has to */ + /* be split into head and body chunks from different RX rings */ + bool rx_packets_compound; + + bool rx_vlan_stripping; + bool lro_supported; + + uint8_t rxq_num; + + /* Network MTU */ + uint32_t mtu; + + /* Maximum number of fragments for indicated TX packets */ + uint32_t max_tx_frags; + + /* Maximum number of fragments for indicated RX packets */ + uint16_t max_rx_frags; + + /* Index for events interrupt */ + uint8_t event_int_idx; + + /* Whether automatic interrupts masking enabled */ + bool auto_int_masking; + + bool peer_has_vhdr; + + /* TX packets to QEMU interface */ + struct VmxnetTxPkt *tx_pkt; + uint32_t offload_mode; + uint32_t cso_or_gso_size; + uint16_t tci; + bool needs_vlan; + + struct VmxnetRxPkt *rx_pkt; + + bool tx_sop; + bool skip_current_tx_pkt; + + uint32_t device_active; + uint32_t last_command; + + uint32_t link_status_and_speed; + + Vmxnet3IntState interrupt_states[VMXNET3_MAX_INTRS]; + + uint32_t temp_mac; /* To store the low part first */ + + MACAddr perm_mac; + uint32_t vlan_table[VMXNET3_VFT_SIZE]; + uint32_t rx_mode; + MACAddr *mcast_list; + uint32_t mcast_list_len; + uint32_t mcast_list_buff_size; /* needed for live migration. */ +} VMXNET3State; + +/* Interrupt management */ + +/* + *This function returns sign whether interrupt line is in asserted state + * This depends on the type of interrupt used. For INTX interrupt line will + * be asserted until explicit deassertion, for MSI(X) interrupt line will + * be deasserted automatically due to notification semantics of the MSI(X) + * interrupts + */ +static bool _vmxnet3_assert_interrupt_line(VMXNET3State *s, uint32_t int_idx) +{ + PCIDevice *d = PCI_DEVICE(s); + + if (s->msix_used && msix_enabled(d)) { + VMW_IRPRN("Sending MSI-X notification for vector %u", int_idx); + msix_notify(d, int_idx); + return false; + } + if (s->msi_used && msi_enabled(d)) { + VMW_IRPRN("Sending MSI notification for vector %u", int_idx); + msi_notify(d, int_idx); + return false; + } + + VMW_IRPRN("Asserting line for interrupt %u", int_idx); + qemu_set_irq(d->irq[int_idx], 1); + return true; +} + +static void _vmxnet3_deassert_interrupt_line(VMXNET3State *s, int lidx) +{ + PCIDevice *d = PCI_DEVICE(s); + + /* + * This function should never be called for MSI(X) interrupts + * because deassertion never required for message interrupts + */ + assert(!s->msix_used || !msix_enabled(d)); + /* + * This function should never be called for MSI(X) interrupts + * because deassertion never required for message interrupts + */ + assert(!s->msi_used || !msi_enabled(d)); + + VMW_IRPRN("Deasserting line for interrupt %u", lidx); + qemu_set_irq(d->irq[lidx], 0); +} + +static void vmxnet3_update_interrupt_line_state(VMXNET3State *s, int lidx) +{ + if (!s->interrupt_states[lidx].is_pending && + s->interrupt_states[lidx].is_asserted) { + VMW_IRPRN("New interrupt line state for index %d is DOWN", lidx); + _vmxnet3_deassert_interrupt_line(s, lidx); + s->interrupt_states[lidx].is_asserted = false; + return; + } + + if (s->interrupt_states[lidx].is_pending && + !s->interrupt_states[lidx].is_masked && + !s->interrupt_states[lidx].is_asserted) { + VMW_IRPRN("New interrupt line state for index %d is UP", lidx); + s->interrupt_states[lidx].is_asserted = + _vmxnet3_assert_interrupt_line(s, lidx); + s->interrupt_states[lidx].is_pending = false; + return; + } +} + +static void vmxnet3_trigger_interrupt(VMXNET3State *s, int lidx) +{ + PCIDevice *d = PCI_DEVICE(s); + s->interrupt_states[lidx].is_pending = true; + vmxnet3_update_interrupt_line_state(s, lidx); + + if (s->msix_used && msix_enabled(d) && s->auto_int_masking) { + goto do_automask; + } + + if (s->msi_used && msi_enabled(d) && s->auto_int_masking) { + goto do_automask; + } + + return; + +do_automask: + s->interrupt_states[lidx].is_masked = true; + vmxnet3_update_interrupt_line_state(s, lidx); +} + +static bool vmxnet3_interrupt_asserted(VMXNET3State *s, int lidx) +{ + return s->interrupt_states[lidx].is_asserted; +} + +static void vmxnet3_clear_interrupt(VMXNET3State *s, int int_idx) +{ + s->interrupt_states[int_idx].is_pending = false; + if (s->auto_int_masking) { + s->interrupt_states[int_idx].is_masked = true; + } + vmxnet3_update_interrupt_line_state(s, int_idx); +} + +static void +vmxnet3_on_interrupt_mask_changed(VMXNET3State *s, int lidx, bool is_masked) +{ + s->interrupt_states[lidx].is_masked = is_masked; + vmxnet3_update_interrupt_line_state(s, lidx); +} + +static bool vmxnet3_verify_driver_magic(hwaddr dshmem) +{ + return (VMXNET3_READ_DRV_SHARED32(dshmem, magic) == VMXNET3_REV1_MAGIC); +} + +#define VMXNET3_GET_BYTE(x, byte_num) (((x) >> (byte_num)*8) & 0xFF) +#define VMXNET3_MAKE_BYTE(byte_num, val) \ + (((uint32_t)((val) & 0xFF)) << (byte_num)*8) + +static void vmxnet3_set_variable_mac(VMXNET3State *s, uint32_t h, uint32_t l) +{ + s->conf.macaddr.a[0] = VMXNET3_GET_BYTE(l, 0); + s->conf.macaddr.a[1] = VMXNET3_GET_BYTE(l, 1); + s->conf.macaddr.a[2] = VMXNET3_GET_BYTE(l, 2); + s->conf.macaddr.a[3] = VMXNET3_GET_BYTE(l, 3); + s->conf.macaddr.a[4] = VMXNET3_GET_BYTE(h, 0); + s->conf.macaddr.a[5] = VMXNET3_GET_BYTE(h, 1); + + VMW_CFPRN("Variable MAC: " VMXNET_MF, VMXNET_MA(s->conf.macaddr.a)); + + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); +} + +static uint64_t vmxnet3_get_mac_low(MACAddr *addr) +{ + return VMXNET3_MAKE_BYTE(0, addr->a[0]) | + VMXNET3_MAKE_BYTE(1, addr->a[1]) | + VMXNET3_MAKE_BYTE(2, addr->a[2]) | + VMXNET3_MAKE_BYTE(3, addr->a[3]); +} + +static uint64_t vmxnet3_get_mac_high(MACAddr *addr) +{ + return VMXNET3_MAKE_BYTE(0, addr->a[4]) | + VMXNET3_MAKE_BYTE(1, addr->a[5]); +} + +static void +vmxnet3_inc_tx_consumption_counter(VMXNET3State *s, int qidx) +{ + vmxnet3_ring_inc(&s->txq_descr[qidx].tx_ring); +} + +static inline void +vmxnet3_inc_rx_consumption_counter(VMXNET3State *s, int qidx, int ridx) +{ + vmxnet3_ring_inc(&s->rxq_descr[qidx].rx_ring[ridx]); +} + +static inline void +vmxnet3_inc_tx_completion_counter(VMXNET3State *s, int qidx) +{ + vmxnet3_ring_inc(&s->txq_descr[qidx].comp_ring); +} + +static void +vmxnet3_inc_rx_completion_counter(VMXNET3State *s, int qidx) +{ + vmxnet3_ring_inc(&s->rxq_descr[qidx].comp_ring); +} + +static void +vmxnet3_dec_rx_completion_counter(VMXNET3State *s, int qidx) +{ + vmxnet3_ring_dec(&s->rxq_descr[qidx].comp_ring); +} + +static void vmxnet3_complete_packet(VMXNET3State *s, int qidx, uint32 tx_ridx) +{ + struct Vmxnet3_TxCompDesc txcq_descr; + + VMXNET3_RING_DUMP(VMW_RIPRN, "TXC", qidx, &s->txq_descr[qidx].comp_ring); + + txcq_descr.txdIdx = tx_ridx; + txcq_descr.gen = vmxnet3_ring_curr_gen(&s->txq_descr[qidx].comp_ring); + + vmxnet3_ring_write_curr_cell(&s->txq_descr[qidx].comp_ring, &txcq_descr); + + /* Flush changes in TX descriptor before changing the counter value */ + smp_wmb(); + + vmxnet3_inc_tx_completion_counter(s, qidx); + vmxnet3_trigger_interrupt(s, s->txq_descr[qidx].intr_idx); +} + +static bool +vmxnet3_setup_tx_offloads(VMXNET3State *s) +{ + switch (s->offload_mode) { + case VMXNET3_OM_NONE: + vmxnet_tx_pkt_build_vheader(s->tx_pkt, false, false, 0); + break; + + case VMXNET3_OM_CSUM: + vmxnet_tx_pkt_build_vheader(s->tx_pkt, false, true, 0); + VMW_PKPRN("L4 CSO requested\n"); + break; + + case VMXNET3_OM_TSO: + vmxnet_tx_pkt_build_vheader(s->tx_pkt, true, true, + s->cso_or_gso_size); + vmxnet_tx_pkt_update_ip_checksums(s->tx_pkt); + VMW_PKPRN("GSO offload requested."); + break; + + default: + assert(false); + return false; + } + + return true; +} + +static void +vmxnet3_tx_retrieve_metadata(VMXNET3State *s, + const struct Vmxnet3_TxDesc *txd) +{ + s->offload_mode = txd->om; + s->cso_or_gso_size = txd->msscof; + s->tci = txd->tci; + s->needs_vlan = txd->ti; +} + +typedef enum { + VMXNET3_PKT_STATUS_OK, + VMXNET3_PKT_STATUS_ERROR, + VMXNET3_PKT_STATUS_DISCARD,/* only for tx */ + VMXNET3_PKT_STATUS_OUT_OF_BUF /* only for rx */ +} Vmxnet3PktStatus; + +static void +vmxnet3_on_tx_done_update_stats(VMXNET3State *s, int qidx, + Vmxnet3PktStatus status) +{ + size_t tot_len = vmxnet_tx_pkt_get_total_len(s->tx_pkt); + struct UPT1_TxStats *stats = &s->txq_descr[qidx].txq_stats; + + switch (status) { + case VMXNET3_PKT_STATUS_OK: + switch (vmxnet_tx_pkt_get_packet_type(s->tx_pkt)) { + case ETH_PKT_BCAST: + stats->bcastPktsTxOK++; + stats->bcastBytesTxOK += tot_len; + break; + case ETH_PKT_MCAST: + stats->mcastPktsTxOK++; + stats->mcastBytesTxOK += tot_len; + break; + case ETH_PKT_UCAST: + stats->ucastPktsTxOK++; + stats->ucastBytesTxOK += tot_len; + break; + default: + assert(false); + } + + if (s->offload_mode == VMXNET3_OM_TSO) { + /* + * According to VMWARE headers this statistic is a number + * of packets after segmentation but since we don't have + * this information in QEMU model, the best we can do is to + * provide number of non-segmented packets + */ + stats->TSOPktsTxOK++; + stats->TSOBytesTxOK += tot_len; + } + break; + + case VMXNET3_PKT_STATUS_DISCARD: + stats->pktsTxDiscard++; + break; + + case VMXNET3_PKT_STATUS_ERROR: + stats->pktsTxError++; + break; + + default: + assert(false); + } +} + +static void +vmxnet3_on_rx_done_update_stats(VMXNET3State *s, + int qidx, + Vmxnet3PktStatus status) +{ + struct UPT1_RxStats *stats = &s->rxq_descr[qidx].rxq_stats; + size_t tot_len = vmxnet_rx_pkt_get_total_len(s->rx_pkt); + + switch (status) { + case VMXNET3_PKT_STATUS_OUT_OF_BUF: + stats->pktsRxOutOfBuf++; + break; + + case VMXNET3_PKT_STATUS_ERROR: + stats->pktsRxError++; + break; + case VMXNET3_PKT_STATUS_OK: + switch (vmxnet_rx_pkt_get_packet_type(s->rx_pkt)) { + case ETH_PKT_BCAST: + stats->bcastPktsRxOK++; + stats->bcastBytesRxOK += tot_len; + break; + case ETH_PKT_MCAST: + stats->mcastPktsRxOK++; + stats->mcastBytesRxOK += tot_len; + break; + case ETH_PKT_UCAST: + stats->ucastPktsRxOK++; + stats->ucastBytesRxOK += tot_len; + break; + default: + assert(false); + } + + if (tot_len > s->mtu) { + stats->LROPktsRxOK++; + stats->LROBytesRxOK += tot_len; + } + break; + default: + assert(false); + } +} + +static inline bool +vmxnet3_pop_next_tx_descr(VMXNET3State *s, + int qidx, + struct Vmxnet3_TxDesc *txd, + uint32_t *descr_idx) +{ + Vmxnet3Ring *ring = &s->txq_descr[qidx].tx_ring; + + vmxnet3_ring_read_curr_cell(ring, txd); + if (txd->gen == vmxnet3_ring_curr_gen(ring)) { + /* Only read after generation field verification */ + smp_rmb(); + /* Re-read to be sure we got the latest version */ + vmxnet3_ring_read_curr_cell(ring, txd); + VMXNET3_RING_DUMP(VMW_RIPRN, "TX", qidx, ring); + *descr_idx = vmxnet3_ring_curr_cell_idx(ring); + vmxnet3_inc_tx_consumption_counter(s, qidx); + return true; + } + + return false; +} + +static bool +vmxnet3_send_packet(VMXNET3State *s, uint32_t qidx) +{ + Vmxnet3PktStatus status = VMXNET3_PKT_STATUS_OK; + + if (!vmxnet3_setup_tx_offloads(s)) { + status = VMXNET3_PKT_STATUS_ERROR; + goto func_exit; + } + + /* debug prints */ + vmxnet3_dump_virt_hdr(vmxnet_tx_pkt_get_vhdr(s->tx_pkt)); + vmxnet_tx_pkt_dump(s->tx_pkt); + + if (!vmxnet_tx_pkt_send(s->tx_pkt, qemu_get_queue(s->nic))) { + status = VMXNET3_PKT_STATUS_DISCARD; + goto func_exit; + } + +func_exit: + vmxnet3_on_tx_done_update_stats(s, qidx, status); + return (status == VMXNET3_PKT_STATUS_OK); +} + +static void vmxnet3_process_tx_queue(VMXNET3State *s, int qidx) +{ + struct Vmxnet3_TxDesc txd; + uint32_t txd_idx; + uint32_t data_len; + hwaddr data_pa; + + for (;;) { + if (!vmxnet3_pop_next_tx_descr(s, qidx, &txd, &txd_idx)) { + break; + } + + vmxnet3_dump_tx_descr(&txd); + + if (!s->skip_current_tx_pkt) { + data_len = (txd.len > 0) ? txd.len : VMXNET3_MAX_TX_BUF_SIZE; + data_pa = le64_to_cpu(txd.addr); + + if (!vmxnet_tx_pkt_add_raw_fragment(s->tx_pkt, + data_pa, + data_len)) { + s->skip_current_tx_pkt = true; + } + } + + if (s->tx_sop) { + vmxnet3_tx_retrieve_metadata(s, &txd); + s->tx_sop = false; + } + + if (txd.eop) { + if (!s->skip_current_tx_pkt) { + vmxnet_tx_pkt_parse(s->tx_pkt); + + if (s->needs_vlan) { + vmxnet_tx_pkt_setup_vlan_header(s->tx_pkt, s->tci); + } + + vmxnet3_send_packet(s, qidx); + } else { + vmxnet3_on_tx_done_update_stats(s, qidx, + VMXNET3_PKT_STATUS_ERROR); + } + + vmxnet3_complete_packet(s, qidx, txd_idx); + s->tx_sop = true; + s->skip_current_tx_pkt = false; + vmxnet_tx_pkt_reset(s->tx_pkt); + } + } +} + +static inline void +vmxnet3_read_next_rx_descr(VMXNET3State *s, int qidx, int ridx, + struct Vmxnet3_RxDesc *dbuf, uint32_t *didx) +{ + Vmxnet3Ring *ring = &s->rxq_descr[qidx].rx_ring[ridx]; + *didx = vmxnet3_ring_curr_cell_idx(ring); + vmxnet3_ring_read_curr_cell(ring, dbuf); +} + +static inline uint8_t +vmxnet3_get_rx_ring_gen(VMXNET3State *s, int qidx, int ridx) +{ + return s->rxq_descr[qidx].rx_ring[ridx].gen; +} + +static inline hwaddr +vmxnet3_pop_rxc_descr(VMXNET3State *s, int qidx, uint32_t *descr_gen) +{ + uint8_t ring_gen; + struct Vmxnet3_RxCompDesc rxcd; + + hwaddr daddr = + vmxnet3_ring_curr_cell_pa(&s->rxq_descr[qidx].comp_ring); + + cpu_physical_memory_read(daddr, &rxcd, sizeof(struct Vmxnet3_RxCompDesc)); + ring_gen = vmxnet3_ring_curr_gen(&s->rxq_descr[qidx].comp_ring); + + if (rxcd.gen != ring_gen) { + *descr_gen = ring_gen; + vmxnet3_inc_rx_completion_counter(s, qidx); + return daddr; + } + + return 0; +} + +static inline void +vmxnet3_revert_rxc_descr(VMXNET3State *s, int qidx) +{ + vmxnet3_dec_rx_completion_counter(s, qidx); +} + +#define RXQ_IDX (0) +#define RX_HEAD_BODY_RING (0) +#define RX_BODY_ONLY_RING (1) + +static bool +vmxnet3_get_next_head_rx_descr(VMXNET3State *s, + struct Vmxnet3_RxDesc *descr_buf, + uint32_t *descr_idx, + uint32_t *ridx) +{ + for (;;) { + uint32_t ring_gen; + vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_HEAD_BODY_RING, + descr_buf, descr_idx); + + /* If no more free descriptors - return */ + ring_gen = vmxnet3_get_rx_ring_gen(s, RXQ_IDX, RX_HEAD_BODY_RING); + if (descr_buf->gen != ring_gen) { + return false; + } + + /* Only read after generation field verification */ + smp_rmb(); + /* Re-read to be sure we got the latest version */ + vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_HEAD_BODY_RING, + descr_buf, descr_idx); + + /* Mark current descriptor as used/skipped */ + vmxnet3_inc_rx_consumption_counter(s, RXQ_IDX, RX_HEAD_BODY_RING); + + /* If this is what we are looking for - return */ + if (descr_buf->btype == VMXNET3_RXD_BTYPE_HEAD) { + *ridx = RX_HEAD_BODY_RING; + return true; + } + } +} + +static bool +vmxnet3_get_next_body_rx_descr(VMXNET3State *s, + struct Vmxnet3_RxDesc *d, + uint32_t *didx, + uint32_t *ridx) +{ + vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_HEAD_BODY_RING, d, didx); + + /* Try to find corresponding descriptor in head/body ring */ + if (d->gen == vmxnet3_get_rx_ring_gen(s, RXQ_IDX, RX_HEAD_BODY_RING)) { + /* Only read after generation field verification */ + smp_rmb(); + /* Re-read to be sure we got the latest version */ + vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_HEAD_BODY_RING, d, didx); + if (d->btype == VMXNET3_RXD_BTYPE_BODY) { + vmxnet3_inc_rx_consumption_counter(s, RXQ_IDX, RX_HEAD_BODY_RING); + *ridx = RX_HEAD_BODY_RING; + return true; + } + } + + /* + * If there is no free descriptors on head/body ring or next free + * descriptor is a head descriptor switch to body only ring + */ + vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_BODY_ONLY_RING, d, didx); + + /* If no more free descriptors - return */ + if (d->gen == vmxnet3_get_rx_ring_gen(s, RXQ_IDX, RX_BODY_ONLY_RING)) { + /* Only read after generation field verification */ + smp_rmb(); + /* Re-read to be sure we got the latest version */ + vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_BODY_ONLY_RING, d, didx); + assert(d->btype == VMXNET3_RXD_BTYPE_BODY); + *ridx = RX_BODY_ONLY_RING; + vmxnet3_inc_rx_consumption_counter(s, RXQ_IDX, RX_BODY_ONLY_RING); + return true; + } + + return false; +} + +static inline bool +vmxnet3_get_next_rx_descr(VMXNET3State *s, bool is_head, + struct Vmxnet3_RxDesc *descr_buf, + uint32_t *descr_idx, + uint32_t *ridx) +{ + if (is_head || !s->rx_packets_compound) { + return vmxnet3_get_next_head_rx_descr(s, descr_buf, descr_idx, ridx); + } else { + return vmxnet3_get_next_body_rx_descr(s, descr_buf, descr_idx, ridx); + } +} + +static void vmxnet3_rx_update_descr(struct VmxnetRxPkt *pkt, + struct Vmxnet3_RxCompDesc *rxcd) +{ + int csum_ok, is_gso; + bool isip4, isip6, istcp, isudp; + struct virtio_net_hdr *vhdr; + uint8_t offload_type; + + if (vmxnet_rx_pkt_is_vlan_stripped(pkt)) { + rxcd->ts = 1; + rxcd->tci = vmxnet_rx_pkt_get_vlan_tag(pkt); + } + + if (!vmxnet_rx_pkt_has_virt_hdr(pkt)) { + goto nocsum; + } + + vhdr = vmxnet_rx_pkt_get_vhdr(pkt); + /* + * Checksum is valid when lower level tell so or when lower level + * requires checksum offload telling that packet produced/bridged + * locally and did travel over network after last checksum calculation + * or production + */ + csum_ok = VMXNET_FLAG_IS_SET(vhdr->flags, VIRTIO_NET_HDR_F_DATA_VALID) || + VMXNET_FLAG_IS_SET(vhdr->flags, VIRTIO_NET_HDR_F_NEEDS_CSUM); + + offload_type = vhdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN; + is_gso = (offload_type != VIRTIO_NET_HDR_GSO_NONE) ? 1 : 0; + + if (!csum_ok && !is_gso) { + goto nocsum; + } + + vmxnet_rx_pkt_get_protocols(pkt, &isip4, &isip6, &isudp, &istcp); + if ((!istcp && !isudp) || (!isip4 && !isip6)) { + goto nocsum; + } + + rxcd->cnc = 0; + rxcd->v4 = isip4 ? 1 : 0; + rxcd->v6 = isip6 ? 1 : 0; + rxcd->tcp = istcp ? 1 : 0; + rxcd->udp = isudp ? 1 : 0; + rxcd->fcs = rxcd->tuc = rxcd->ipc = 1; + return; + +nocsum: + rxcd->cnc = 1; + return; +} + +static void +vmxnet3_physical_memory_writev(const struct iovec *iov, + size_t start_iov_off, + hwaddr target_addr, + size_t bytes_to_copy) +{ + size_t curr_off = 0; + size_t copied = 0; + + while (bytes_to_copy) { + if (start_iov_off < (curr_off + iov->iov_len)) { + size_t chunk_len = + MIN((curr_off + iov->iov_len) - start_iov_off, bytes_to_copy); + + cpu_physical_memory_write(target_addr + copied, + iov->iov_base + start_iov_off - curr_off, + chunk_len); + + copied += chunk_len; + start_iov_off += chunk_len; + curr_off = start_iov_off; + bytes_to_copy -= chunk_len; + } else { + curr_off += iov->iov_len; + } + iov++; + } +} + +static bool +vmxnet3_indicate_packet(VMXNET3State *s) +{ + struct Vmxnet3_RxDesc rxd; + bool is_head = true; + uint32_t rxd_idx; + uint32_t rx_ridx; + + struct Vmxnet3_RxCompDesc rxcd; + uint32_t new_rxcd_gen = VMXNET3_INIT_GEN; + hwaddr new_rxcd_pa = 0; + hwaddr ready_rxcd_pa = 0; + struct iovec *data = vmxnet_rx_pkt_get_iovec(s->rx_pkt); + size_t bytes_copied = 0; + size_t bytes_left = vmxnet_rx_pkt_get_total_len(s->rx_pkt); + uint16_t num_frags = 0; + size_t chunk_size; + + vmxnet_rx_pkt_dump(s->rx_pkt); + + while (bytes_left > 0) { + + /* cannot add more frags to packet */ + if (num_frags == s->max_rx_frags) { + break; + } + + new_rxcd_pa = vmxnet3_pop_rxc_descr(s, RXQ_IDX, &new_rxcd_gen); + if (!new_rxcd_pa) { + break; + } + + if (!vmxnet3_get_next_rx_descr(s, is_head, &rxd, &rxd_idx, &rx_ridx)) { + break; + } + + chunk_size = MIN(bytes_left, rxd.len); + vmxnet3_physical_memory_writev(data, bytes_copied, + le64_to_cpu(rxd.addr), chunk_size); + bytes_copied += chunk_size; + bytes_left -= chunk_size; + + vmxnet3_dump_rx_descr(&rxd); + + if (0 != ready_rxcd_pa) { + cpu_physical_memory_write(ready_rxcd_pa, &rxcd, sizeof(rxcd)); + } + + memset(&rxcd, 0, sizeof(struct Vmxnet3_RxCompDesc)); + rxcd.rxdIdx = rxd_idx; + rxcd.len = chunk_size; + rxcd.sop = is_head; + rxcd.gen = new_rxcd_gen; + rxcd.rqID = RXQ_IDX + rx_ridx * s->rxq_num; + + if (0 == bytes_left) { + vmxnet3_rx_update_descr(s->rx_pkt, &rxcd); + } + + VMW_RIPRN("RX Completion descriptor: rxRing: %lu rxIdx %lu len %lu " + "sop %d csum_correct %lu", + (unsigned long) rx_ridx, + (unsigned long) rxcd.rxdIdx, + (unsigned long) rxcd.len, + (int) rxcd.sop, + (unsigned long) rxcd.tuc); + + is_head = false; + ready_rxcd_pa = new_rxcd_pa; + new_rxcd_pa = 0; + } + + if (0 != ready_rxcd_pa) { + rxcd.eop = 1; + rxcd.err = (0 != bytes_left); + cpu_physical_memory_write(ready_rxcd_pa, &rxcd, sizeof(rxcd)); + + /* Flush RX descriptor changes */ + smp_wmb(); + } + + if (0 != new_rxcd_pa) { + vmxnet3_revert_rxc_descr(s, RXQ_IDX); + } + + vmxnet3_trigger_interrupt(s, s->rxq_descr[RXQ_IDX].intr_idx); + + if (bytes_left == 0) { + vmxnet3_on_rx_done_update_stats(s, RXQ_IDX, VMXNET3_PKT_STATUS_OK); + return true; + } else if (num_frags == s->max_rx_frags) { + vmxnet3_on_rx_done_update_stats(s, RXQ_IDX, VMXNET3_PKT_STATUS_ERROR); + return false; + } else { + vmxnet3_on_rx_done_update_stats(s, RXQ_IDX, + VMXNET3_PKT_STATUS_OUT_OF_BUF); + return false; + } +} + +static void +vmxnet3_io_bar0_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + VMXNET3State *s = opaque; + + if (VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_TXPROD, + VMXNET3_DEVICE_MAX_TX_QUEUES, VMXNET3_REG_ALIGN)) { + int tx_queue_idx = + VMW_MULTIREG_IDX_BY_ADDR(addr, VMXNET3_REG_TXPROD, + VMXNET3_REG_ALIGN); + assert(tx_queue_idx <= s->txq_num); + vmxnet3_process_tx_queue(s, tx_queue_idx); + return; + } + + if (VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_IMR, + VMXNET3_MAX_INTRS, VMXNET3_REG_ALIGN)) { + int l = VMW_MULTIREG_IDX_BY_ADDR(addr, VMXNET3_REG_IMR, + VMXNET3_REG_ALIGN); + + VMW_CBPRN("Interrupt mask for line %d written: 0x%" PRIx64, l, val); + + vmxnet3_on_interrupt_mask_changed(s, l, val); + return; + } + + if (VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_RXPROD, + VMXNET3_DEVICE_MAX_RX_QUEUES, VMXNET3_REG_ALIGN) || + VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_RXPROD2, + VMXNET3_DEVICE_MAX_RX_QUEUES, VMXNET3_REG_ALIGN)) { + return; + } + + VMW_WRPRN("BAR0 unknown write [%" PRIx64 "] = %" PRIx64 ", size %d", + (uint64_t) addr, val, size); +} + +static uint64_t +vmxnet3_io_bar0_read(void *opaque, hwaddr addr, unsigned size) +{ + if (VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_IMR, + VMXNET3_MAX_INTRS, VMXNET3_REG_ALIGN)) { + assert(false); + } + + VMW_CBPRN("BAR0 unknown read [%" PRIx64 "], size %d", addr, size); + return 0; +} + +static void vmxnet3_reset_interrupt_states(VMXNET3State *s) +{ + int i; + for (i = 0; i < ARRAY_SIZE(s->interrupt_states); i++) { + s->interrupt_states[i].is_asserted = false; + s->interrupt_states[i].is_pending = false; + s->interrupt_states[i].is_masked = true; + } +} + +static void vmxnet3_reset_mac(VMXNET3State *s) +{ + memcpy(&s->conf.macaddr.a, &s->perm_mac.a, sizeof(s->perm_mac.a)); + VMW_CFPRN("MAC address set to: " VMXNET_MF, VMXNET_MA(s->conf.macaddr.a)); +} + +static void vmxnet3_deactivate_device(VMXNET3State *s) +{ + VMW_CBPRN("Deactivating vmxnet3..."); + s->device_active = false; +} + +static void vmxnet3_reset(VMXNET3State *s) +{ + VMW_CBPRN("Resetting vmxnet3..."); + + vmxnet3_deactivate_device(s); + vmxnet3_reset_interrupt_states(s); + vmxnet_tx_pkt_reset(s->tx_pkt); + s->drv_shmem = 0; + s->tx_sop = true; + s->skip_current_tx_pkt = false; +} + +static void vmxnet3_update_rx_mode(VMXNET3State *s) +{ + s->rx_mode = VMXNET3_READ_DRV_SHARED32(s->drv_shmem, + devRead.rxFilterConf.rxMode); + VMW_CFPRN("RX mode: 0x%08X", s->rx_mode); +} + +static void vmxnet3_update_vlan_filters(VMXNET3State *s) +{ + int i; + + /* Copy configuration from shared memory */ + VMXNET3_READ_DRV_SHARED(s->drv_shmem, + devRead.rxFilterConf.vfTable, + s->vlan_table, + sizeof(s->vlan_table)); + + /* Invert byte order when needed */ + for (i = 0; i < ARRAY_SIZE(s->vlan_table); i++) { + s->vlan_table[i] = le32_to_cpu(s->vlan_table[i]); + } + + /* Dump configuration for debugging purposes */ + VMW_CFPRN("Configured VLANs:"); + for (i = 0; i < sizeof(s->vlan_table) * 8; i++) { + if (VMXNET3_VFTABLE_ENTRY_IS_SET(s->vlan_table, i)) { + VMW_CFPRN("\tVLAN %d is present", i); + } + } +} + +static void vmxnet3_update_mcast_filters(VMXNET3State *s) +{ + uint16_t list_bytes = + VMXNET3_READ_DRV_SHARED16(s->drv_shmem, + devRead.rxFilterConf.mfTableLen); + + s->mcast_list_len = list_bytes / sizeof(s->mcast_list[0]); + + s->mcast_list = g_realloc(s->mcast_list, list_bytes); + if (NULL == s->mcast_list) { + if (0 == s->mcast_list_len) { + VMW_CFPRN("Current multicast list is empty"); + } else { + VMW_ERPRN("Failed to allocate multicast list of %d elements", + s->mcast_list_len); + } + s->mcast_list_len = 0; + } else { + int i; + hwaddr mcast_list_pa = + VMXNET3_READ_DRV_SHARED64(s->drv_shmem, + devRead.rxFilterConf.mfTablePA); + + cpu_physical_memory_read(mcast_list_pa, s->mcast_list, list_bytes); + VMW_CFPRN("Current multicast list len is %d:", s->mcast_list_len); + for (i = 0; i < s->mcast_list_len; i++) { + VMW_CFPRN("\t" VMXNET_MF, VMXNET_MA(s->mcast_list[i].a)); + } + } +} + +static void vmxnet3_setup_rx_filtering(VMXNET3State *s) +{ + vmxnet3_update_rx_mode(s); + vmxnet3_update_vlan_filters(s); + vmxnet3_update_mcast_filters(s); +} + +static uint32_t vmxnet3_get_interrupt_config(VMXNET3State *s) +{ + uint32_t interrupt_mode = VMXNET3_IT_AUTO | (VMXNET3_IMM_AUTO << 2); + VMW_CFPRN("Interrupt config is 0x%X", interrupt_mode); + return interrupt_mode; +} + +static void vmxnet3_fill_stats(VMXNET3State *s) +{ + int i; + for (i = 0; i < s->txq_num; i++) { + cpu_physical_memory_write(s->txq_descr[i].tx_stats_pa, + &s->txq_descr[i].txq_stats, + sizeof(s->txq_descr[i].txq_stats)); + } + + for (i = 0; i < s->rxq_num; i++) { + cpu_physical_memory_write(s->rxq_descr[i].rx_stats_pa, + &s->rxq_descr[i].rxq_stats, + sizeof(s->rxq_descr[i].rxq_stats)); + } +} + +static void vmxnet3_adjust_by_guest_type(VMXNET3State *s) +{ + struct Vmxnet3_GOSInfo gos; + + VMXNET3_READ_DRV_SHARED(s->drv_shmem, devRead.misc.driverInfo.gos, + &gos, sizeof(gos)); + s->rx_packets_compound = + (gos.gosType == VMXNET3_GOS_TYPE_WIN) ? false : true; + + VMW_CFPRN("Guest type specifics: RXCOMPOUND: %d", s->rx_packets_compound); +} + +static void +vmxnet3_dump_conf_descr(const char *name, + struct Vmxnet3_VariableLenConfDesc *pm_descr) +{ + VMW_CFPRN("%s descriptor dump: Version %u, Length %u", + name, pm_descr->confVer, pm_descr->confLen); + +}; + +static void vmxnet3_update_pm_state(VMXNET3State *s) +{ + struct Vmxnet3_VariableLenConfDesc pm_descr; + + pm_descr.confLen = + VMXNET3_READ_DRV_SHARED32(s->drv_shmem, devRead.pmConfDesc.confLen); + pm_descr.confVer = + VMXNET3_READ_DRV_SHARED32(s->drv_shmem, devRead.pmConfDesc.confVer); + pm_descr.confPA = + VMXNET3_READ_DRV_SHARED64(s->drv_shmem, devRead.pmConfDesc.confPA); + + vmxnet3_dump_conf_descr("PM State", &pm_descr); +} + +static void vmxnet3_update_features(VMXNET3State *s) +{ + uint32_t guest_features; + int rxcso_supported; + + guest_features = VMXNET3_READ_DRV_SHARED32(s->drv_shmem, + devRead.misc.uptFeatures); + + rxcso_supported = VMXNET_FLAG_IS_SET(guest_features, UPT1_F_RXCSUM); + s->rx_vlan_stripping = VMXNET_FLAG_IS_SET(guest_features, UPT1_F_RXVLAN); + s->lro_supported = VMXNET_FLAG_IS_SET(guest_features, UPT1_F_LRO); + + VMW_CFPRN("Features configuration: LRO: %d, RXCSUM: %d, VLANSTRIP: %d", + s->lro_supported, rxcso_supported, + s->rx_vlan_stripping); + if (s->peer_has_vhdr) { + tap_set_offload(qemu_get_queue(s->nic)->peer, + rxcso_supported, + s->lro_supported, + s->lro_supported, + 0, + 0); + } +} + +static void vmxnet3_activate_device(VMXNET3State *s) +{ + int i; + static const uint32_t VMXNET3_DEF_TX_THRESHOLD = 1; + hwaddr qdescr_table_pa; + uint64_t pa; + uint32_t size; + + /* Verify configuration consistency */ + if (!vmxnet3_verify_driver_magic(s->drv_shmem)) { + VMW_ERPRN("Device configuration received from driver is invalid"); + return; + } + + vmxnet3_adjust_by_guest_type(s); + vmxnet3_update_features(s); + vmxnet3_update_pm_state(s); + vmxnet3_setup_rx_filtering(s); + /* Cache fields from shared memory */ + s->mtu = VMXNET3_READ_DRV_SHARED32(s->drv_shmem, devRead.misc.mtu); + VMW_CFPRN("MTU is %u", s->mtu); + + s->max_rx_frags = + VMXNET3_READ_DRV_SHARED16(s->drv_shmem, devRead.misc.maxNumRxSG); + + VMW_CFPRN("Max RX fragments is %u", s->max_rx_frags); + + s->event_int_idx = + VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.intrConf.eventIntrIdx); + VMW_CFPRN("Events interrupt line is %u", s->event_int_idx); + + s->auto_int_masking = + VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.intrConf.autoMask); + VMW_CFPRN("Automatic interrupt masking is %d", (int)s->auto_int_masking); + + s->txq_num = + VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.misc.numTxQueues); + s->rxq_num = + VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.misc.numRxQueues); + + VMW_CFPRN("Number of TX/RX queues %u/%u", s->txq_num, s->rxq_num); + assert(s->txq_num <= VMXNET3_DEVICE_MAX_TX_QUEUES); + + qdescr_table_pa = + VMXNET3_READ_DRV_SHARED64(s->drv_shmem, devRead.misc.queueDescPA); + VMW_CFPRN("TX queues descriptors table is at 0x%" PRIx64, qdescr_table_pa); + + /* + * Worst-case scenario is a packet that holds all TX rings space so + * we calculate total size of all TX rings for max TX fragments number + */ + s->max_tx_frags = 0; + + /* TX queues */ + for (i = 0; i < s->txq_num; i++) { + hwaddr qdescr_pa = + qdescr_table_pa + i * sizeof(struct Vmxnet3_TxQueueDesc); + + /* Read interrupt number for this TX queue */ + s->txq_descr[i].intr_idx = + VMXNET3_READ_TX_QUEUE_DESCR8(qdescr_pa, conf.intrIdx); + + VMW_CFPRN("TX Queue %d interrupt: %d", i, s->txq_descr[i].intr_idx); + + /* Read rings memory locations for TX queues */ + pa = VMXNET3_READ_TX_QUEUE_DESCR64(qdescr_pa, conf.txRingBasePA); + size = VMXNET3_READ_TX_QUEUE_DESCR32(qdescr_pa, conf.txRingSize); + + vmxnet3_ring_init(&s->txq_descr[i].tx_ring, pa, size, + sizeof(struct Vmxnet3_TxDesc), false); + VMXNET3_RING_DUMP(VMW_CFPRN, "TX", i, &s->txq_descr[i].tx_ring); + + s->max_tx_frags += size; + + /* TXC ring */ + pa = VMXNET3_READ_TX_QUEUE_DESCR64(qdescr_pa, conf.compRingBasePA); + size = VMXNET3_READ_TX_QUEUE_DESCR32(qdescr_pa, conf.compRingSize); + vmxnet3_ring_init(&s->txq_descr[i].comp_ring, pa, size, + sizeof(struct Vmxnet3_TxCompDesc), true); + VMXNET3_RING_DUMP(VMW_CFPRN, "TXC", i, &s->txq_descr[i].comp_ring); + + s->txq_descr[i].tx_stats_pa = + qdescr_pa + offsetof(struct Vmxnet3_TxQueueDesc, stats); + + memset(&s->txq_descr[i].txq_stats, 0, + sizeof(s->txq_descr[i].txq_stats)); + + /* Fill device-managed parameters for queues */ + VMXNET3_WRITE_TX_QUEUE_DESCR32(qdescr_pa, + ctrl.txThreshold, + VMXNET3_DEF_TX_THRESHOLD); + } + + /* Preallocate TX packet wrapper */ + VMW_CFPRN("Max TX fragments is %u", s->max_tx_frags); + vmxnet_tx_pkt_init(&s->tx_pkt, s->max_tx_frags, s->peer_has_vhdr); + vmxnet_rx_pkt_init(&s->rx_pkt, s->peer_has_vhdr); + + /* Read rings memory locations for RX queues */ + for (i = 0; i < s->rxq_num; i++) { + int j; + hwaddr qd_pa = + qdescr_table_pa + s->txq_num * sizeof(struct Vmxnet3_TxQueueDesc) + + i * sizeof(struct Vmxnet3_RxQueueDesc); + + /* Read interrupt number for this RX queue */ + s->rxq_descr[i].intr_idx = + VMXNET3_READ_TX_QUEUE_DESCR8(qd_pa, conf.intrIdx); + + VMW_CFPRN("RX Queue %d interrupt: %d", i, s->rxq_descr[i].intr_idx); + + /* Read rings memory locations */ + for (j = 0; j < VMXNET3_RX_RINGS_PER_QUEUE; j++) { + /* RX rings */ + pa = VMXNET3_READ_RX_QUEUE_DESCR64(qd_pa, conf.rxRingBasePA[j]); + size = VMXNET3_READ_RX_QUEUE_DESCR32(qd_pa, conf.rxRingSize[j]); + vmxnet3_ring_init(&s->rxq_descr[i].rx_ring[j], pa, size, + sizeof(struct Vmxnet3_RxDesc), false); + VMW_CFPRN("RX queue %d:%d: Base: %" PRIx64 ", Size: %d", + i, j, pa, size); + } + + /* RXC ring */ + pa = VMXNET3_READ_RX_QUEUE_DESCR64(qd_pa, conf.compRingBasePA); + size = VMXNET3_READ_RX_QUEUE_DESCR32(qd_pa, conf.compRingSize); + vmxnet3_ring_init(&s->rxq_descr[i].comp_ring, pa, size, + sizeof(struct Vmxnet3_RxCompDesc), true); + VMW_CFPRN("RXC queue %d: Base: %" PRIx64 ", Size: %d", i, pa, size); + + s->rxq_descr[i].rx_stats_pa = + qd_pa + offsetof(struct Vmxnet3_RxQueueDesc, stats); + memset(&s->rxq_descr[i].rxq_stats, 0, + sizeof(s->rxq_descr[i].rxq_stats)); + } + + /* Make sure everything is in place before device activation */ + smp_wmb(); + + vmxnet3_reset_mac(s); + + s->device_active = true; +} + +static void vmxnet3_handle_command(VMXNET3State *s, uint64_t cmd) +{ + s->last_command = cmd; + + switch (cmd) { + case VMXNET3_CMD_GET_PERM_MAC_HI: + VMW_CBPRN("Set: Get upper part of permanent MAC"); + break; + + case VMXNET3_CMD_GET_PERM_MAC_LO: + VMW_CBPRN("Set: Get lower part of permanent MAC"); + break; + + case VMXNET3_CMD_GET_STATS: + VMW_CBPRN("Set: Get device statistics"); + vmxnet3_fill_stats(s); + break; + + case VMXNET3_CMD_ACTIVATE_DEV: + VMW_CBPRN("Set: Activating vmxnet3 device"); + vmxnet3_activate_device(s); + break; + + case VMXNET3_CMD_UPDATE_RX_MODE: + VMW_CBPRN("Set: Update rx mode"); + vmxnet3_update_rx_mode(s); + break; + + case VMXNET3_CMD_UPDATE_VLAN_FILTERS: + VMW_CBPRN("Set: Update VLAN filters"); + vmxnet3_update_vlan_filters(s); + break; + + case VMXNET3_CMD_UPDATE_MAC_FILTERS: + VMW_CBPRN("Set: Update MAC filters"); + vmxnet3_update_mcast_filters(s); + break; + + case VMXNET3_CMD_UPDATE_FEATURE: + VMW_CBPRN("Set: Update features"); + vmxnet3_update_features(s); + break; + + case VMXNET3_CMD_UPDATE_PMCFG: + VMW_CBPRN("Set: Update power management config"); + vmxnet3_update_pm_state(s); + break; + + case VMXNET3_CMD_GET_LINK: + VMW_CBPRN("Set: Get link"); + break; + + case VMXNET3_CMD_RESET_DEV: + VMW_CBPRN("Set: Reset device"); + vmxnet3_reset(s); + break; + + case VMXNET3_CMD_QUIESCE_DEV: + VMW_CBPRN("Set: VMXNET3_CMD_QUIESCE_DEV - pause the device"); + vmxnet3_deactivate_device(s); + break; + + case VMXNET3_CMD_GET_CONF_INTR: + VMW_CBPRN("Set: VMXNET3_CMD_GET_CONF_INTR - interrupt configuration"); + break; + + default: + VMW_CBPRN("Received unknown command: %" PRIx64, cmd); + break; + } +} + +static uint64_t vmxnet3_get_command_status(VMXNET3State *s) +{ + uint64_t ret; + + switch (s->last_command) { + case VMXNET3_CMD_ACTIVATE_DEV: + ret = (s->device_active) ? 0 : -1; + VMW_CFPRN("Device active: %" PRIx64, ret); + break; + + case VMXNET3_CMD_GET_LINK: + ret = s->link_status_and_speed; + VMW_CFPRN("Link and speed: %" PRIx64, ret); + break; + + case VMXNET3_CMD_GET_PERM_MAC_LO: + ret = vmxnet3_get_mac_low(&s->perm_mac); + break; + + case VMXNET3_CMD_GET_PERM_MAC_HI: + ret = vmxnet3_get_mac_high(&s->perm_mac); + break; + + case VMXNET3_CMD_GET_CONF_INTR: + ret = vmxnet3_get_interrupt_config(s); + break; + + default: + VMW_WRPRN("Received request for unknown command: %x", s->last_command); + ret = -1; + break; + } + + return ret; +} + +static void vmxnet3_set_events(VMXNET3State *s, uint32_t val) +{ + uint32_t events; + + VMW_CBPRN("Setting events: 0x%x", val); + events = VMXNET3_READ_DRV_SHARED32(s->drv_shmem, ecr) | val; + VMXNET3_WRITE_DRV_SHARED32(s->drv_shmem, ecr, events); +} + +static void vmxnet3_ack_events(VMXNET3State *s, uint32_t val) +{ + uint32_t events; + + VMW_CBPRN("Clearing events: 0x%x", val); + events = VMXNET3_READ_DRV_SHARED32(s->drv_shmem, ecr) & ~val; + VMXNET3_WRITE_DRV_SHARED32(s->drv_shmem, ecr, events); +} + +static void +vmxnet3_io_bar1_write(void *opaque, + hwaddr addr, + uint64_t val, + unsigned size) +{ + VMXNET3State *s = opaque; + + switch (addr) { + /* Vmxnet3 Revision Report Selection */ + case VMXNET3_REG_VRRS: + VMW_CBPRN("Write BAR1 [VMXNET3_REG_VRRS] = %" PRIx64 ", size %d", + val, size); + break; + + /* UPT Version Report Selection */ + case VMXNET3_REG_UVRS: + VMW_CBPRN("Write BAR1 [VMXNET3_REG_UVRS] = %" PRIx64 ", size %d", + val, size); + break; + + /* Driver Shared Address Low */ + case VMXNET3_REG_DSAL: + VMW_CBPRN("Write BAR1 [VMXNET3_REG_DSAL] = %" PRIx64 ", size %d", + val, size); + /* + * Guest driver will first write the low part of the shared + * memory address. We save it to temp variable and set the + * shared address only after we get the high part + */ + if (0 == val) { + s->device_active = false; + } + s->temp_shared_guest_driver_memory = val; + s->drv_shmem = 0; + break; + + /* Driver Shared Address High */ + case VMXNET3_REG_DSAH: + VMW_CBPRN("Write BAR1 [VMXNET3_REG_DSAH] = %" PRIx64 ", size %d", + val, size); + /* + * Set the shared memory between guest driver and device. + * We already should have low address part. + */ + s->drv_shmem = s->temp_shared_guest_driver_memory | (val << 32); + break; + + /* Command */ + case VMXNET3_REG_CMD: + VMW_CBPRN("Write BAR1 [VMXNET3_REG_CMD] = %" PRIx64 ", size %d", + val, size); + vmxnet3_handle_command(s, val); + break; + + /* MAC Address Low */ + case VMXNET3_REG_MACL: + VMW_CBPRN("Write BAR1 [VMXNET3_REG_MACL] = %" PRIx64 ", size %d", + val, size); + s->temp_mac = val; + break; + + /* MAC Address High */ + case VMXNET3_REG_MACH: + VMW_CBPRN("Write BAR1 [VMXNET3_REG_MACH] = %" PRIx64 ", size %d", + val, size); + vmxnet3_set_variable_mac(s, val, s->temp_mac); + break; + + /* Interrupt Cause Register */ + case VMXNET3_REG_ICR: + VMW_CBPRN("Write BAR1 [VMXNET3_REG_ICR] = %" PRIx64 ", size %d", + val, size); + assert(false); + break; + + /* Event Cause Register */ + case VMXNET3_REG_ECR: + VMW_CBPRN("Write BAR1 [VMXNET3_REG_ECR] = %" PRIx64 ", size %d", + val, size); + vmxnet3_ack_events(s, val); + break; + + default: + VMW_CBPRN("Unknown Write to BAR1 [%" PRIx64 "] = %" PRIx64 ", size %d", + addr, val, size); + break; + } +} + +static uint64_t +vmxnet3_io_bar1_read(void *opaque, hwaddr addr, unsigned size) +{ + VMXNET3State *s = opaque; + uint64_t ret = 0; + + switch (addr) { + /* Vmxnet3 Revision Report Selection */ + case VMXNET3_REG_VRRS: + VMW_CBPRN("Read BAR1 [VMXNET3_REG_VRRS], size %d", size); + ret = VMXNET3_DEVICE_REVISION; + break; + + /* UPT Version Report Selection */ + case VMXNET3_REG_UVRS: + VMW_CBPRN("Read BAR1 [VMXNET3_REG_UVRS], size %d", size); + ret = VMXNET3_DEVICE_VERSION; + break; + + /* Command */ + case VMXNET3_REG_CMD: + VMW_CBPRN("Read BAR1 [VMXNET3_REG_CMD], size %d", size); + ret = vmxnet3_get_command_status(s); + break; + + /* MAC Address Low */ + case VMXNET3_REG_MACL: + VMW_CBPRN("Read BAR1 [VMXNET3_REG_MACL], size %d", size); + ret = vmxnet3_get_mac_low(&s->conf.macaddr); + break; + + /* MAC Address High */ + case VMXNET3_REG_MACH: + VMW_CBPRN("Read BAR1 [VMXNET3_REG_MACH], size %d", size); + ret = vmxnet3_get_mac_high(&s->conf.macaddr); + break; + + /* + * Interrupt Cause Register + * Used for legacy interrupts only so interrupt index always 0 + */ + case VMXNET3_REG_ICR: + VMW_CBPRN("Read BAR1 [VMXNET3_REG_ICR], size %d", size); + if (vmxnet3_interrupt_asserted(s, 0)) { + vmxnet3_clear_interrupt(s, 0); + ret = true; + } else { + ret = false; + } + break; + + default: + VMW_CBPRN("Unknow read BAR1[%" PRIx64 "], %d bytes", addr, size); + break; + } + + return ret; +} + +static int +vmxnet3_can_receive(NetClientState *nc) +{ + VMXNET3State *s = qemu_get_nic_opaque(nc); + return s->device_active && + VMXNET_FLAG_IS_SET(s->link_status_and_speed, VMXNET3_LINK_STATUS_UP); +} + +static inline bool +vmxnet3_is_registered_vlan(VMXNET3State *s, const void *data) +{ + uint16_t vlan_tag = eth_get_pkt_tci(data) & VLAN_VID_MASK; + if (IS_SPECIAL_VLAN_ID(vlan_tag)) { + return true; + } + + return VMXNET3_VFTABLE_ENTRY_IS_SET(s->vlan_table, vlan_tag); +} + +static bool +vmxnet3_is_allowed_mcast_group(VMXNET3State *s, const uint8_t *group_mac) +{ + int i; + for (i = 0; i < s->mcast_list_len; i++) { + if (!memcmp(group_mac, s->mcast_list[i].a, sizeof(s->mcast_list[i]))) { + return true; + } + } + return false; +} + +static bool +vmxnet3_rx_filter_may_indicate(VMXNET3State *s, const void *data, + size_t size) +{ + struct eth_header *ehdr = PKT_GET_ETH_HDR(data); + + if (VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_PROMISC)) { + return true; + } + + if (!vmxnet3_is_registered_vlan(s, data)) { + return false; + } + + switch (vmxnet_rx_pkt_get_packet_type(s->rx_pkt)) { + case ETH_PKT_UCAST: + if (!VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_UCAST)) { + return false; + } + if (memcmp(s->conf.macaddr.a, ehdr->h_dest, ETH_ALEN)) { + return false; + } + break; + + case ETH_PKT_BCAST: + if (!VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_BCAST)) { + return false; + } + break; + + case ETH_PKT_MCAST: + if (VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_ALL_MULTI)) { + return true; + } + if (!VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_MCAST)) { + return false; + } + if (!vmxnet3_is_allowed_mcast_group(s, ehdr->h_dest)) { + return false; + } + break; + + default: + assert(false); + } + + return true; +} + +static ssize_t +vmxnet3_receive(NetClientState *nc, const uint8_t *buf, size_t size) +{ + VMXNET3State *s = qemu_get_nic_opaque(nc); + size_t bytes_indicated; + + if (!vmxnet3_can_receive(nc)) { + VMW_PKPRN("Cannot receive now"); + return -1; + } + + if (s->peer_has_vhdr) { + vmxnet_rx_pkt_set_vhdr(s->rx_pkt, (struct virtio_net_hdr *)buf); + buf += sizeof(struct virtio_net_hdr); + size -= sizeof(struct virtio_net_hdr); + } + + vmxnet_rx_pkt_set_packet_type(s->rx_pkt, + get_eth_packet_type(PKT_GET_ETH_HDR(buf))); + + if (vmxnet3_rx_filter_may_indicate(s, buf, size)) { + vmxnet_rx_pkt_attach_data(s->rx_pkt, buf, size, s->rx_vlan_stripping); + bytes_indicated = vmxnet3_indicate_packet(s) ? size : -1; + if (bytes_indicated < size) { + VMW_PKPRN("RX: %lu of %lu bytes indicated", bytes_indicated, size); + } + } else { + VMW_PKPRN("Packet dropped by RX filter"); + bytes_indicated = size; + } + + assert(size > 0); + assert(bytes_indicated != 0); + return bytes_indicated; +} + +static void vmxnet3_cleanup(NetClientState *nc) +{ + VMXNET3State *s = qemu_get_nic_opaque(nc); + s->nic = NULL; +} + +static void vmxnet3_set_link_status(NetClientState *nc) +{ + VMXNET3State *s = qemu_get_nic_opaque(nc); + + if (nc->link_down) { + s->link_status_and_speed &= ~VMXNET3_LINK_STATUS_UP; + } else { + s->link_status_and_speed |= VMXNET3_LINK_STATUS_UP; + } + + vmxnet3_set_events(s, VMXNET3_ECR_LINK); + vmxnet3_trigger_interrupt(s, s->event_int_idx); +} + +static NetClientInfo net_vmxnet3_info = { + .type = NET_CLIENT_OPTIONS_KIND_NIC, + .size = sizeof(NICState), + .can_receive = vmxnet3_can_receive, + .receive = vmxnet3_receive, + .cleanup = vmxnet3_cleanup, + .link_status_changed = vmxnet3_set_link_status, +}; + +static bool vmxnet3_peer_has_vnet_hdr(VMXNET3State *s) +{ + NetClientState *peer = qemu_get_queue(s->nic)->peer; + + if ((NULL != peer) && + (peer->info->type == NET_CLIENT_OPTIONS_KIND_TAP) && + tap_has_vnet_hdr(peer)) { + return true; + } + + VMW_WRPRN("Peer has no virtio extension. Task offloads will be emulated."); + return false; +} + +static void vmxnet3_net_uninit(VMXNET3State *s) +{ + g_free(s->mcast_list); + vmxnet_tx_pkt_reset(s->tx_pkt); + vmxnet_tx_pkt_uninit(s->tx_pkt); + vmxnet_rx_pkt_uninit(s->rx_pkt); + qemu_del_net_client(qemu_get_queue(s->nic)); +} + +static void vmxnet3_net_init(VMXNET3State *s) +{ + DeviceState *d = DEVICE(s); + + VMW_CBPRN("vmxnet3_net_init called..."); + + qemu_macaddr_default_if_unset(&s->conf.macaddr); + + /* Windows guest will query the address that was set on init */ + memcpy(&s->perm_mac.a, &s->conf.macaddr.a, sizeof(s->perm_mac.a)); + + s->mcast_list = NULL; + s->mcast_list_len = 0; + + s->link_status_and_speed = VMXNET3_LINK_SPEED | VMXNET3_LINK_STATUS_UP; + + VMW_CFPRN("Permanent MAC: " MAC_FMT, MAC_ARG(s->perm_mac.a)); + + s->nic = qemu_new_nic(&net_vmxnet3_info, &s->conf, + object_get_typename(OBJECT(s)), + d->id, s); + + s->peer_has_vhdr = vmxnet3_peer_has_vnet_hdr(s); + s->tx_sop = true; + s->skip_current_tx_pkt = false; + s->tx_pkt = NULL; + s->rx_pkt = NULL; + s->rx_vlan_stripping = false; + s->lro_supported = false; + + if (s->peer_has_vhdr) { + tap_set_vnet_hdr_len(qemu_get_queue(s->nic)->peer, + sizeof(struct virtio_net_hdr)); + + tap_using_vnet_hdr(qemu_get_queue(s->nic)->peer, 1); + } + + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); +} + +static void +vmxnet3_unuse_msix_vectors(VMXNET3State *s, int num_vectors) +{ + PCIDevice *d = PCI_DEVICE(s); + int i; + for (i = 0; i < num_vectors; i++) { + msix_vector_unuse(d, i); + } +} + +static bool +vmxnet3_use_msix_vectors(VMXNET3State *s, int num_vectors) +{ + PCIDevice *d = PCI_DEVICE(s); + int i; + for (i = 0; i < num_vectors; i++) { + int res = msix_vector_use(d, i); + if (0 > res) { + VMW_WRPRN("Failed to use MSI-X vector %d, error %d", i, res); + vmxnet3_unuse_msix_vectors(s, i); + return false; + } + } + return true; +} + +static bool +vmxnet3_init_msix(VMXNET3State *s) +{ + PCIDevice *d = PCI_DEVICE(s); + int res = msix_init(d, VMXNET3_MAX_INTRS, + &s->msix_bar, + VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_TABLE, + &s->msix_bar, + VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_PBA, + 0); + + if (0 > res) { + VMW_WRPRN("Failed to initialize MSI-X, error %d", res); + s->msix_used = false; + } else { + if (!vmxnet3_use_msix_vectors(s, VMXNET3_MAX_INTRS)) { + VMW_WRPRN("Failed to use MSI-X vectors, error %d", res); + msix_uninit(d, &s->msix_bar, &s->msix_bar); + s->msix_used = false; + } else { + s->msix_used = true; + } + } + return s->msix_used; +} + +static void +vmxnet3_cleanup_msix(VMXNET3State *s) +{ + PCIDevice *d = PCI_DEVICE(s); + + if (s->msix_used) { + msix_vector_unuse(d, VMXNET3_MAX_INTRS); + msix_uninit(d, &s->msix_bar, &s->msix_bar); + } +} + +#define VMXNET3_MSI_NUM_VECTORS (1) +#define VMXNET3_MSI_OFFSET (0x50) +#define VMXNET3_USE_64BIT (true) +#define VMXNET3_PER_VECTOR_MASK (false) + +static bool +vmxnet3_init_msi(VMXNET3State *s) +{ + PCIDevice *d = PCI_DEVICE(s); + int res; + + res = msi_init(d, VMXNET3_MSI_OFFSET, VMXNET3_MSI_NUM_VECTORS, + VMXNET3_USE_64BIT, VMXNET3_PER_VECTOR_MASK); + if (0 > res) { + VMW_WRPRN("Failed to initialize MSI, error %d", res); + s->msi_used = false; + } else { + s->msi_used = true; + } + + return s->msi_used; +} + +static void +vmxnet3_cleanup_msi(VMXNET3State *s) +{ + PCIDevice *d = PCI_DEVICE(s); + + if (s->msi_used) { + msi_uninit(d); + } +} + +static void +vmxnet3_msix_save(QEMUFile *f, void *opaque) +{ + PCIDevice *d = PCI_DEVICE(opaque); + msix_save(d, f); +} + +static int +vmxnet3_msix_load(QEMUFile *f, void *opaque, int version_id) +{ + PCIDevice *d = PCI_DEVICE(opaque); + msix_load(d, f); + return 0; +} + +static const MemoryRegionOps b0_ops = { + .read = vmxnet3_io_bar0_read, + .write = vmxnet3_io_bar0_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static const MemoryRegionOps b1_ops = { + .read = vmxnet3_io_bar1_read, + .write = vmxnet3_io_bar1_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static int vmxnet3_pci_init(PCIDevice *pci_dev) +{ + DeviceState *dev = DEVICE(pci_dev); + VMXNET3State *s = VMXNET3(pci_dev); + + VMW_CBPRN("Starting init..."); + + memory_region_init_io(&s->bar0, &b0_ops, s, + "vmxnet3-b0", VMXNET3_PT_REG_SIZE); + pci_register_bar(pci_dev, VMXNET3_BAR0_IDX, + PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar0); + + memory_region_init_io(&s->bar1, &b1_ops, s, + "vmxnet3-b1", VMXNET3_VD_REG_SIZE); + pci_register_bar(pci_dev, VMXNET3_BAR1_IDX, + PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar1); + + memory_region_init(&s->msix_bar, "vmxnet3-msix-bar", + VMXNET3_MSIX_BAR_SIZE); + pci_register_bar(pci_dev, VMXNET3_MSIX_BAR_IDX, + PCI_BASE_ADDRESS_SPACE_MEMORY, &s->msix_bar); + + vmxnet3_reset_interrupt_states(s); + + /* Interrupt pin A */ + pci_dev->config[PCI_INTERRUPT_PIN] = 0x01; + + if (!vmxnet3_init_msix(s)) { + VMW_WRPRN("Failed to initialize MSI-X, configuration is inconsistent."); + } + + if (!vmxnet3_init_msi(s)) { + VMW_WRPRN("Failed to initialize MSI, configuration is inconsistent."); + } + + vmxnet3_net_init(s); + + register_savevm(dev, "vmxnet3-msix", -1, 1, + vmxnet3_msix_save, vmxnet3_msix_load, s); + + add_boot_device_path(s->conf.bootindex, dev, "/ethernet-phy@0"); + + return 0; +} + + +static void vmxnet3_pci_uninit(PCIDevice *pci_dev) +{ + DeviceState *dev = DEVICE(pci_dev); + VMXNET3State *s = VMXNET3(pci_dev); + + VMW_CBPRN("Starting uninit..."); + + unregister_savevm(dev, "vmxnet3-msix", s); + + vmxnet3_net_uninit(s); + + vmxnet3_cleanup_msix(s); + + vmxnet3_cleanup_msi(s); + + memory_region_destroy(&s->bar0); + memory_region_destroy(&s->bar1); + memory_region_destroy(&s->msix_bar); +} + +static void vmxnet3_qdev_reset(DeviceState *dev) +{ + PCIDevice *d = PCI_DEVICE(dev); + VMXNET3State *s = VMXNET3(d); + + VMW_CBPRN("Starting QDEV reset..."); + vmxnet3_reset(s); +} + +static bool vmxnet3_mc_list_needed(void *opaque) +{ + return true; +} + +static int vmxnet3_mcast_list_pre_load(void *opaque) +{ + VMXNET3State *s = opaque; + + s->mcast_list = g_malloc(s->mcast_list_buff_size); + + return 0; +} + + +static void vmxnet3_pre_save(void *opaque) +{ + VMXNET3State *s = opaque; + + s->mcast_list_buff_size = s->mcast_list_len * sizeof(MACAddr); +} + +static const VMStateDescription vmxstate_vmxnet3_mcast_list = { + .name = "vmxnet3/mcast_list", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .pre_load = vmxnet3_mcast_list_pre_load, + .fields = (VMStateField[]) { + VMSTATE_VBUFFER_UINT32(mcast_list, VMXNET3State, 0, NULL, 0, + mcast_list_buff_size), + VMSTATE_END_OF_LIST() + } +}; + +static void vmxnet3_get_ring_from_file(QEMUFile *f, Vmxnet3Ring *r) +{ + r->pa = qemu_get_be64(f); + r->size = qemu_get_be32(f); + r->cell_size = qemu_get_be32(f); + r->next = qemu_get_be32(f); + r->gen = qemu_get_byte(f); +} + +static void vmxnet3_put_ring_to_file(QEMUFile *f, Vmxnet3Ring *r) +{ + qemu_put_be64(f, r->pa); + qemu_put_be32(f, r->size); + qemu_put_be32(f, r->cell_size); + qemu_put_be32(f, r->next); + qemu_put_byte(f, r->gen); +} + +static void vmxnet3_get_tx_stats_from_file(QEMUFile *f, + struct UPT1_TxStats *tx_stat) +{ + tx_stat->TSOPktsTxOK = qemu_get_be64(f); + tx_stat->TSOBytesTxOK = qemu_get_be64(f); + tx_stat->ucastPktsTxOK = qemu_get_be64(f); + tx_stat->ucastBytesTxOK = qemu_get_be64(f); + tx_stat->mcastPktsTxOK = qemu_get_be64(f); + tx_stat->mcastBytesTxOK = qemu_get_be64(f); + tx_stat->bcastPktsTxOK = qemu_get_be64(f); + tx_stat->bcastBytesTxOK = qemu_get_be64(f); + tx_stat->pktsTxError = qemu_get_be64(f); + tx_stat->pktsTxDiscard = qemu_get_be64(f); +} + +static void vmxnet3_put_tx_stats_to_file(QEMUFile *f, + struct UPT1_TxStats *tx_stat) +{ + qemu_put_be64(f, tx_stat->TSOPktsTxOK); + qemu_put_be64(f, tx_stat->TSOBytesTxOK); + qemu_put_be64(f, tx_stat->ucastPktsTxOK); + qemu_put_be64(f, tx_stat->ucastBytesTxOK); + qemu_put_be64(f, tx_stat->mcastPktsTxOK); + qemu_put_be64(f, tx_stat->mcastBytesTxOK); + qemu_put_be64(f, tx_stat->bcastPktsTxOK); + qemu_put_be64(f, tx_stat->bcastBytesTxOK); + qemu_put_be64(f, tx_stat->pktsTxError); + qemu_put_be64(f, tx_stat->pktsTxDiscard); +} + +static int vmxnet3_get_txq_descr(QEMUFile *f, void *pv, size_t size) +{ + Vmxnet3TxqDescr *r = pv; + + vmxnet3_get_ring_from_file(f, &r->tx_ring); + vmxnet3_get_ring_from_file(f, &r->comp_ring); + r->intr_idx = qemu_get_byte(f); + r->tx_stats_pa = qemu_get_be64(f); + + vmxnet3_get_tx_stats_from_file(f, &r->txq_stats); + + return 0; +} + +static void vmxnet3_put_txq_descr(QEMUFile *f, void *pv, size_t size) +{ + Vmxnet3TxqDescr *r = pv; + + vmxnet3_put_ring_to_file(f, &r->tx_ring); + vmxnet3_put_ring_to_file(f, &r->comp_ring); + qemu_put_byte(f, r->intr_idx); + qemu_put_be64(f, r->tx_stats_pa); + vmxnet3_put_tx_stats_to_file(f, &r->txq_stats); +} + +const VMStateInfo txq_descr_info = { + .name = "txq_descr", + .get = vmxnet3_get_txq_descr, + .put = vmxnet3_put_txq_descr +}; + +static void vmxnet3_get_rx_stats_from_file(QEMUFile *f, + struct UPT1_RxStats *rx_stat) +{ + rx_stat->LROPktsRxOK = qemu_get_be64(f); + rx_stat->LROBytesRxOK = qemu_get_be64(f); + rx_stat->ucastPktsRxOK = qemu_get_be64(f); + rx_stat->ucastBytesRxOK = qemu_get_be64(f); + rx_stat->mcastPktsRxOK = qemu_get_be64(f); + rx_stat->mcastBytesRxOK = qemu_get_be64(f); + rx_stat->bcastPktsRxOK = qemu_get_be64(f); + rx_stat->bcastBytesRxOK = qemu_get_be64(f); + rx_stat->pktsRxOutOfBuf = qemu_get_be64(f); + rx_stat->pktsRxError = qemu_get_be64(f); +} + +static void vmxnet3_put_rx_stats_to_file(QEMUFile *f, + struct UPT1_RxStats *rx_stat) +{ + qemu_put_be64(f, rx_stat->LROPktsRxOK); + qemu_put_be64(f, rx_stat->LROBytesRxOK); + qemu_put_be64(f, rx_stat->ucastPktsRxOK); + qemu_put_be64(f, rx_stat->ucastBytesRxOK); + qemu_put_be64(f, rx_stat->mcastPktsRxOK); + qemu_put_be64(f, rx_stat->mcastBytesRxOK); + qemu_put_be64(f, rx_stat->bcastPktsRxOK); + qemu_put_be64(f, rx_stat->bcastBytesRxOK); + qemu_put_be64(f, rx_stat->pktsRxOutOfBuf); + qemu_put_be64(f, rx_stat->pktsRxError); +} + +static int vmxnet3_get_rxq_descr(QEMUFile *f, void *pv, size_t size) +{ + Vmxnet3RxqDescr *r = pv; + int i; + + for (i = 0; i < VMXNET3_RX_RINGS_PER_QUEUE; i++) { + vmxnet3_get_ring_from_file(f, &r->rx_ring[i]); + } + + vmxnet3_get_ring_from_file(f, &r->comp_ring); + r->intr_idx = qemu_get_byte(f); + r->rx_stats_pa = qemu_get_be64(f); + + vmxnet3_get_rx_stats_from_file(f, &r->rxq_stats); + + return 0; +} + +static void vmxnet3_put_rxq_descr(QEMUFile *f, void *pv, size_t size) +{ + Vmxnet3RxqDescr *r = pv; + int i; + + for (i = 0; i < VMXNET3_RX_RINGS_PER_QUEUE; i++) { + vmxnet3_put_ring_to_file(f, &r->rx_ring[i]); + } + + vmxnet3_put_ring_to_file(f, &r->comp_ring); + qemu_put_byte(f, r->intr_idx); + qemu_put_be64(f, r->rx_stats_pa); + vmxnet3_put_rx_stats_to_file(f, &r->rxq_stats); +} + +static int vmxnet3_post_load(void *opaque, int version_id) +{ + VMXNET3State *s = opaque; + PCIDevice *d = PCI_DEVICE(s); + + vmxnet_tx_pkt_init(&s->tx_pkt, s->max_tx_frags, s->peer_has_vhdr); + vmxnet_rx_pkt_init(&s->rx_pkt, s->peer_has_vhdr); + + if (s->msix_used) { + if (!vmxnet3_use_msix_vectors(s, VMXNET3_MAX_INTRS)) { + VMW_WRPRN("Failed to re-use MSI-X vectors"); + msix_uninit(d, &s->msix_bar, &s->msix_bar); + s->msix_used = false; + return -1; + } + } + + return 0; +} + +const VMStateInfo rxq_descr_info = { + .name = "rxq_descr", + .get = vmxnet3_get_rxq_descr, + .put = vmxnet3_put_rxq_descr +}; + +static int vmxnet3_get_int_state(QEMUFile *f, void *pv, size_t size) +{ + Vmxnet3IntState *r = pv; + + r->is_masked = qemu_get_byte(f); + r->is_pending = qemu_get_byte(f); + r->is_asserted = qemu_get_byte(f); + + return 0; +} + +static void vmxnet3_put_int_state(QEMUFile *f, void *pv, size_t size) +{ + Vmxnet3IntState *r = pv; + + qemu_put_byte(f, r->is_masked); + qemu_put_byte(f, r->is_pending); + qemu_put_byte(f, r->is_asserted); +} + +const VMStateInfo int_state_info = { + .name = "int_state", + .get = vmxnet3_get_int_state, + .put = vmxnet3_put_int_state +}; + +static const VMStateDescription vmstate_vmxnet3 = { + .name = "vmxnet3", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .pre_save = vmxnet3_pre_save, + .post_load = vmxnet3_post_load, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj, VMXNET3State), + VMSTATE_BOOL(rx_packets_compound, VMXNET3State), + VMSTATE_BOOL(rx_vlan_stripping, VMXNET3State), + VMSTATE_BOOL(lro_supported, VMXNET3State), + VMSTATE_UINT32(rx_mode, VMXNET3State), + VMSTATE_UINT32(mcast_list_len, VMXNET3State), + VMSTATE_UINT32(mcast_list_buff_size, VMXNET3State), + VMSTATE_UINT32_ARRAY(vlan_table, VMXNET3State, VMXNET3_VFT_SIZE), + VMSTATE_UINT32(mtu, VMXNET3State), + VMSTATE_UINT16(max_rx_frags, VMXNET3State), + VMSTATE_UINT32(max_tx_frags, VMXNET3State), + VMSTATE_UINT8(event_int_idx, VMXNET3State), + VMSTATE_BOOL(auto_int_masking, VMXNET3State), + VMSTATE_UINT8(txq_num, VMXNET3State), + VMSTATE_UINT8(rxq_num, VMXNET3State), + VMSTATE_UINT32(device_active, VMXNET3State), + VMSTATE_UINT32(last_command, VMXNET3State), + VMSTATE_UINT32(link_status_and_speed, VMXNET3State), + VMSTATE_UINT32(temp_mac, VMXNET3State), + VMSTATE_UINT64(drv_shmem, VMXNET3State), + VMSTATE_UINT64(temp_shared_guest_driver_memory, VMXNET3State), + + VMSTATE_ARRAY(txq_descr, VMXNET3State, + VMXNET3_DEVICE_MAX_TX_QUEUES, 0, txq_descr_info, + Vmxnet3TxqDescr), + VMSTATE_ARRAY(rxq_descr, VMXNET3State, + VMXNET3_DEVICE_MAX_RX_QUEUES, 0, rxq_descr_info, + Vmxnet3RxqDescr), + VMSTATE_ARRAY(interrupt_states, VMXNET3State, VMXNET3_MAX_INTRS, + 0, int_state_info, Vmxnet3IntState), + + VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection[]) { + { + .vmsd = &vmxstate_vmxnet3_mcast_list, + .needed = vmxnet3_mc_list_needed + }, + { + /* empty element. */ + } + } +}; + +static void +vmxnet3_write_config(PCIDevice *pci_dev, uint32_t addr, uint32_t val, int len) +{ + pci_default_write_config(pci_dev, addr, val, len); + msix_write_config(pci_dev, addr, val, len); + msi_write_config(pci_dev, addr, val, len); +} + +static Property vmxnet3_properties[] = { + DEFINE_NIC_PROPERTIES(VMXNET3State, conf), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vmxnet3_class_init(ObjectClass *class, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(class); + PCIDeviceClass *c = PCI_DEVICE_CLASS(class); + + c->init = vmxnet3_pci_init; + c->exit = vmxnet3_pci_uninit; + c->vendor_id = PCI_VENDOR_ID_VMWARE; + c->device_id = PCI_DEVICE_ID_VMWARE_VMXNET3; + c->revision = PCI_DEVICE_ID_VMWARE_VMXNET3_REVISION; + c->class_id = PCI_CLASS_NETWORK_ETHERNET; + c->subsystem_vendor_id = PCI_VENDOR_ID_VMWARE; + c->subsystem_id = PCI_DEVICE_ID_VMWARE_VMXNET3; + c->config_write = vmxnet3_write_config, + dc->desc = "VMWare Paravirtualized Ethernet v3"; + dc->reset = vmxnet3_qdev_reset; + dc->vmsd = &vmstate_vmxnet3; + dc->props = vmxnet3_properties; +} + +static const TypeInfo vmxnet3_info = { + .name = TYPE_VMXNET3, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(VMXNET3State), + .class_init = vmxnet3_class_init, +}; + +static void vmxnet3_register_types(void) +{ + VMW_CBPRN("vmxnet3_register_types called..."); + type_register_static(&vmxnet3_info); +} + +type_init(vmxnet3_register_types) diff --git a/hw/vmxnet3.h b/hw/vmxnet3.h new file mode 100644 index 0000000000..7db0c8f5e0 --- /dev/null +++ b/hw/vmxnet3.h @@ -0,0 +1,760 @@ +/* + * QEMU VMWARE VMXNET3 paravirtual NIC interface definitions + * + * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) + * + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Dmitry Fleytman <dmitry@daynix.com> + * Tamir Shomer <tamirs@daynix.com> + * Yan Vugenfirer <yan@daynix.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef _QEMU_VMXNET3_H +#define _QEMU_VMXNET3_H + +#define VMXNET3_DEVICE_MAX_TX_QUEUES 8 +#define VMXNET3_DEVICE_MAX_RX_QUEUES 8 /* Keep this value as a power of 2 */ + +/* + * VMWARE headers we got from Linux kernel do not fully comply QEMU coding + * standards in sense of types and defines used. + * Since we didn't want to change VMWARE code, following set of typedefs + * and defines needed to compile these headers with QEMU introduced. + */ +#define u64 uint64_t +#define u32 uint32_t +#define u16 uint16_t +#define u8 uint8_t +#define __le16 uint16_t +#define __le32 uint32_t +#define __le64 uint64_t +#define __packed QEMU_PACKED + +#if defined(HOST_WORDS_BIGENDIAN) +#define const_cpu_to_le64(x) bswap_64(x) +#define __BIG_ENDIAN_BITFIELD +#else +#define const_cpu_to_le64(x) (x) +#endif + +/* + * Following is an interface definition for + * VMXNET3 device as provided by VMWARE + * See original copyright from Linux kernel v3.2.8 + * header file drivers/net/vmxnet3/vmxnet3_defs.h below. + */ + +/* + * Linux driver for VMware's vmxnet3 ethernet NIC. + * + * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; version 2 of the License and no later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintained by: Shreyas Bhatewara <pv-drivers@vmware.com> + * + */ + +struct UPT1_TxStats { + u64 TSOPktsTxOK; /* TSO pkts post-segmentation */ + u64 TSOBytesTxOK; + u64 ucastPktsTxOK; + u64 ucastBytesTxOK; + u64 mcastPktsTxOK; + u64 mcastBytesTxOK; + u64 bcastPktsTxOK; + u64 bcastBytesTxOK; + u64 pktsTxError; + u64 pktsTxDiscard; +}; + +struct UPT1_RxStats { + u64 LROPktsRxOK; /* LRO pkts */ + u64 LROBytesRxOK; /* bytes from LRO pkts */ + /* the following counters are for pkts from the wire, i.e., pre-LRO */ + u64 ucastPktsRxOK; + u64 ucastBytesRxOK; + u64 mcastPktsRxOK; + u64 mcastBytesRxOK; + u64 bcastPktsRxOK; + u64 bcastBytesRxOK; + u64 pktsRxOutOfBuf; + u64 pktsRxError; +}; + +/* interrupt moderation level */ +enum { + UPT1_IML_NONE = 0, /* no interrupt moderation */ + UPT1_IML_HIGHEST = 7, /* least intr generated */ + UPT1_IML_ADAPTIVE = 8, /* adpative intr moderation */ +}; +/* values for UPT1_RSSConf.hashFunc */ +enum { + UPT1_RSS_HASH_TYPE_NONE = 0x0, + UPT1_RSS_HASH_TYPE_IPV4 = 0x01, + UPT1_RSS_HASH_TYPE_TCP_IPV4 = 0x02, + UPT1_RSS_HASH_TYPE_IPV6 = 0x04, + UPT1_RSS_HASH_TYPE_TCP_IPV6 = 0x08, +}; + +enum { + UPT1_RSS_HASH_FUNC_NONE = 0x0, + UPT1_RSS_HASH_FUNC_TOEPLITZ = 0x01, +}; + +#define UPT1_RSS_MAX_KEY_SIZE 40 +#define UPT1_RSS_MAX_IND_TABLE_SIZE 128 + +struct UPT1_RSSConf { + u16 hashType; + u16 hashFunc; + u16 hashKeySize; + u16 indTableSize; + u8 hashKey[UPT1_RSS_MAX_KEY_SIZE]; + u8 indTable[UPT1_RSS_MAX_IND_TABLE_SIZE]; +}; + +/* features */ +enum { + UPT1_F_RXCSUM = const_cpu_to_le64(0x0001), /* rx csum verification */ + UPT1_F_RSS = const_cpu_to_le64(0x0002), + UPT1_F_RXVLAN = const_cpu_to_le64(0x0004), /* VLAN tag stripping */ + UPT1_F_LRO = const_cpu_to_le64(0x0008), +}; + +/* all registers are 32 bit wide */ +/* BAR 1 */ +enum { + VMXNET3_REG_VRRS = 0x0, /* Vmxnet3 Revision Report Selection */ + VMXNET3_REG_UVRS = 0x8, /* UPT Version Report Selection */ + VMXNET3_REG_DSAL = 0x10, /* Driver Shared Address Low */ + VMXNET3_REG_DSAH = 0x18, /* Driver Shared Address High */ + VMXNET3_REG_CMD = 0x20, /* Command */ + VMXNET3_REG_MACL = 0x28, /* MAC Address Low */ + VMXNET3_REG_MACH = 0x30, /* MAC Address High */ + VMXNET3_REG_ICR = 0x38, /* Interrupt Cause Register */ + VMXNET3_REG_ECR = 0x40 /* Event Cause Register */ +}; + +/* BAR 0 */ +enum { + VMXNET3_REG_IMR = 0x0, /* Interrupt Mask Register */ + VMXNET3_REG_TXPROD = 0x600, /* Tx Producer Index */ + VMXNET3_REG_RXPROD = 0x800, /* Rx Producer Index for ring 1 */ + VMXNET3_REG_RXPROD2 = 0xA00 /* Rx Producer Index for ring 2 */ +}; + +#define VMXNET3_PT_REG_SIZE 4096 /* BAR 0 */ +#define VMXNET3_VD_REG_SIZE 4096 /* BAR 1 */ + +#define VMXNET3_REG_ALIGN 8 /* All registers are 8-byte aligned. */ +#define VMXNET3_REG_ALIGN_MASK 0x7 + +/* I/O Mapped access to registers */ +#define VMXNET3_IO_TYPE_PT 0 +#define VMXNET3_IO_TYPE_VD 1 +#define VMXNET3_IO_ADDR(type, reg) (((type) << 24) | ((reg) & 0xFFFFFF)) +#define VMXNET3_IO_TYPE(addr) ((addr) >> 24) +#define VMXNET3_IO_REG(addr) ((addr) & 0xFFFFFF) + +enum { + VMXNET3_CMD_FIRST_SET = 0xCAFE0000, + VMXNET3_CMD_ACTIVATE_DEV = VMXNET3_CMD_FIRST_SET, /* 0xCAFE0000 */ + VMXNET3_CMD_QUIESCE_DEV, /* 0xCAFE0001 */ + VMXNET3_CMD_RESET_DEV, /* 0xCAFE0002 */ + VMXNET3_CMD_UPDATE_RX_MODE, /* 0xCAFE0003 */ + VMXNET3_CMD_UPDATE_MAC_FILTERS, /* 0xCAFE0004 */ + VMXNET3_CMD_UPDATE_VLAN_FILTERS, /* 0xCAFE0005 */ + VMXNET3_CMD_UPDATE_RSSIDT, /* 0xCAFE0006 */ + VMXNET3_CMD_UPDATE_IML, /* 0xCAFE0007 */ + VMXNET3_CMD_UPDATE_PMCFG, /* 0xCAFE0008 */ + VMXNET3_CMD_UPDATE_FEATURE, /* 0xCAFE0009 */ + VMXNET3_CMD_LOAD_PLUGIN, /* 0xCAFE000A */ + + VMXNET3_CMD_FIRST_GET = 0xF00D0000, + VMXNET3_CMD_GET_QUEUE_STATUS = VMXNET3_CMD_FIRST_GET, /* 0xF00D0000 */ + VMXNET3_CMD_GET_STATS, /* 0xF00D0001 */ + VMXNET3_CMD_GET_LINK, /* 0xF00D0002 */ + VMXNET3_CMD_GET_PERM_MAC_LO, /* 0xF00D0003 */ + VMXNET3_CMD_GET_PERM_MAC_HI, /* 0xF00D0004 */ + VMXNET3_CMD_GET_DID_LO, /* 0xF00D0005 */ + VMXNET3_CMD_GET_DID_HI, /* 0xF00D0006 */ + VMXNET3_CMD_GET_DEV_EXTRA_INFO, /* 0xF00D0007 */ + VMXNET3_CMD_GET_CONF_INTR /* 0xF00D0008 */ +}; + +/* + * Little Endian layout of bitfields - + * Byte 0 : 7.....len.....0 + * Byte 1 : rsvd gen 13.len.8 + * Byte 2 : 5.msscof.0 ext1 dtype + * Byte 3 : 13...msscof...6 + * + * Big Endian layout of bitfields - + * Byte 0: 13...msscof...6 + * Byte 1 : 5.msscof.0 ext1 dtype + * Byte 2 : rsvd gen 13.len.8 + * Byte 3 : 7.....len.....0 + * + * Thus, le32_to_cpu on the dword will allow the big endian driver to read + * the bit fields correctly. And cpu_to_le32 will convert bitfields + * bit fields written by big endian driver to format required by device. + */ + +struct Vmxnet3_TxDesc { + __le64 addr; + +#ifdef __BIG_ENDIAN_BITFIELD + u32 msscof:14; /* MSS, checksum offset, flags */ + u32 ext1:1; + u32 dtype:1; /* descriptor type */ + u32 rsvd:1; + u32 gen:1; /* generation bit */ + u32 len:14; +#else + u32 len:14; + u32 gen:1; /* generation bit */ + u32 rsvd:1; + u32 dtype:1; /* descriptor type */ + u32 ext1:1; + u32 msscof:14; /* MSS, checksum offset, flags */ +#endif /* __BIG_ENDIAN_BITFIELD */ + +#ifdef __BIG_ENDIAN_BITFIELD + u32 tci:16; /* Tag to Insert */ + u32 ti:1; /* VLAN Tag Insertion */ + u32 ext2:1; + u32 cq:1; /* completion request */ + u32 eop:1; /* End Of Packet */ + u32 om:2; /* offload mode */ + u32 hlen:10; /* header len */ +#else + u32 hlen:10; /* header len */ + u32 om:2; /* offload mode */ + u32 eop:1; /* End Of Packet */ + u32 cq:1; /* completion request */ + u32 ext2:1; + u32 ti:1; /* VLAN Tag Insertion */ + u32 tci:16; /* Tag to Insert */ +#endif /* __BIG_ENDIAN_BITFIELD */ +}; + +/* TxDesc.OM values */ +#define VMXNET3_OM_NONE 0 +#define VMXNET3_OM_CSUM 2 +#define VMXNET3_OM_TSO 3 + +/* fields in TxDesc we access w/o using bit fields */ +#define VMXNET3_TXD_EOP_SHIFT 12 +#define VMXNET3_TXD_CQ_SHIFT 13 +#define VMXNET3_TXD_GEN_SHIFT 14 +#define VMXNET3_TXD_EOP_DWORD_SHIFT 3 +#define VMXNET3_TXD_GEN_DWORD_SHIFT 2 + +#define VMXNET3_TXD_CQ (1 << VMXNET3_TXD_CQ_SHIFT) +#define VMXNET3_TXD_EOP (1 << VMXNET3_TXD_EOP_SHIFT) +#define VMXNET3_TXD_GEN (1 << VMXNET3_TXD_GEN_SHIFT) + +#define VMXNET3_HDR_COPY_SIZE 128 + + +struct Vmxnet3_TxDataDesc { + u8 data[VMXNET3_HDR_COPY_SIZE]; +}; + +#define VMXNET3_TCD_GEN_SHIFT 31 +#define VMXNET3_TCD_GEN_SIZE 1 +#define VMXNET3_TCD_TXIDX_SHIFT 0 +#define VMXNET3_TCD_TXIDX_SIZE 12 +#define VMXNET3_TCD_GEN_DWORD_SHIFT 3 + +struct Vmxnet3_TxCompDesc { + u32 txdIdx:12; /* Index of the EOP TxDesc */ + u32 ext1:20; + + __le32 ext2; + __le32 ext3; + + u32 rsvd:24; + u32 type:7; /* completion type */ + u32 gen:1; /* generation bit */ +}; + +struct Vmxnet3_RxDesc { + __le64 addr; + +#ifdef __BIG_ENDIAN_BITFIELD + u32 gen:1; /* Generation bit */ + u32 rsvd:15; + u32 dtype:1; /* Descriptor type */ + u32 btype:1; /* Buffer Type */ + u32 len:14; +#else + u32 len:14; + u32 btype:1; /* Buffer Type */ + u32 dtype:1; /* Descriptor type */ + u32 rsvd:15; + u32 gen:1; /* Generation bit */ +#endif + u32 ext1; +}; + +/* values of RXD.BTYPE */ +#define VMXNET3_RXD_BTYPE_HEAD 0 /* head only */ +#define VMXNET3_RXD_BTYPE_BODY 1 /* body only */ + +/* fields in RxDesc we access w/o using bit fields */ +#define VMXNET3_RXD_BTYPE_SHIFT 14 +#define VMXNET3_RXD_GEN_SHIFT 31 + +struct Vmxnet3_RxCompDesc { +#ifdef __BIG_ENDIAN_BITFIELD + u32 ext2:1; + u32 cnc:1; /* Checksum Not Calculated */ + u32 rssType:4; /* RSS hash type used */ + u32 rqID:10; /* rx queue/ring ID */ + u32 sop:1; /* Start of Packet */ + u32 eop:1; /* End of Packet */ + u32 ext1:2; + u32 rxdIdx:12; /* Index of the RxDesc */ +#else + u32 rxdIdx:12; /* Index of the RxDesc */ + u32 ext1:2; + u32 eop:1; /* End of Packet */ + u32 sop:1; /* Start of Packet */ + u32 rqID:10; /* rx queue/ring ID */ + u32 rssType:4; /* RSS hash type used */ + u32 cnc:1; /* Checksum Not Calculated */ + u32 ext2:1; +#endif /* __BIG_ENDIAN_BITFIELD */ + + __le32 rssHash; /* RSS hash value */ + +#ifdef __BIG_ENDIAN_BITFIELD + u32 tci:16; /* Tag stripped */ + u32 ts:1; /* Tag is stripped */ + u32 err:1; /* Error */ + u32 len:14; /* data length */ +#else + u32 len:14; /* data length */ + u32 err:1; /* Error */ + u32 ts:1; /* Tag is stripped */ + u32 tci:16; /* Tag stripped */ +#endif /* __BIG_ENDIAN_BITFIELD */ + + +#ifdef __BIG_ENDIAN_BITFIELD + u32 gen:1; /* generation bit */ + u32 type:7; /* completion type */ + u32 fcs:1; /* Frame CRC correct */ + u32 frg:1; /* IP Fragment */ + u32 v4:1; /* IPv4 */ + u32 v6:1; /* IPv6 */ + u32 ipc:1; /* IP Checksum Correct */ + u32 tcp:1; /* TCP packet */ + u32 udp:1; /* UDP packet */ + u32 tuc:1; /* TCP/UDP Checksum Correct */ + u32 csum:16; +#else + u32 csum:16; + u32 tuc:1; /* TCP/UDP Checksum Correct */ + u32 udp:1; /* UDP packet */ + u32 tcp:1; /* TCP packet */ + u32 ipc:1; /* IP Checksum Correct */ + u32 v6:1; /* IPv6 */ + u32 v4:1; /* IPv4 */ + u32 frg:1; /* IP Fragment */ + u32 fcs:1; /* Frame CRC correct */ + u32 type:7; /* completion type */ + u32 gen:1; /* generation bit */ +#endif /* __BIG_ENDIAN_BITFIELD */ +}; + +/* fields in RxCompDesc we access via Vmxnet3_GenericDesc.dword[3] */ +#define VMXNET3_RCD_TUC_SHIFT 16 +#define VMXNET3_RCD_IPC_SHIFT 19 + +/* fields in RxCompDesc we access via Vmxnet3_GenericDesc.qword[1] */ +#define VMXNET3_RCD_TYPE_SHIFT 56 +#define VMXNET3_RCD_GEN_SHIFT 63 + +/* csum OK for TCP/UDP pkts over IP */ +#define VMXNET3_RCD_CSUM_OK (1 << VMXNET3_RCD_TUC_SHIFT | \ + 1 << VMXNET3_RCD_IPC_SHIFT) +#define VMXNET3_TXD_GEN_SIZE 1 +#define VMXNET3_TXD_EOP_SIZE 1 + +/* value of RxCompDesc.rssType */ +enum { + VMXNET3_RCD_RSS_TYPE_NONE = 0, + VMXNET3_RCD_RSS_TYPE_IPV4 = 1, + VMXNET3_RCD_RSS_TYPE_TCPIPV4 = 2, + VMXNET3_RCD_RSS_TYPE_IPV6 = 3, + VMXNET3_RCD_RSS_TYPE_TCPIPV6 = 4, +}; + + +/* a union for accessing all cmd/completion descriptors */ +union Vmxnet3_GenericDesc { + __le64 qword[2]; + __le32 dword[4]; + __le16 word[8]; + struct Vmxnet3_TxDesc txd; + struct Vmxnet3_RxDesc rxd; + struct Vmxnet3_TxCompDesc tcd; + struct Vmxnet3_RxCompDesc rcd; +}; + +#define VMXNET3_INIT_GEN 1 + +/* Max size of a single tx buffer */ +#define VMXNET3_MAX_TX_BUF_SIZE (1 << 14) + +/* # of tx desc needed for a tx buffer size */ +#define VMXNET3_TXD_NEEDED(size) (((size) + VMXNET3_MAX_TX_BUF_SIZE - 1) / \ + VMXNET3_MAX_TX_BUF_SIZE) + +/* max # of tx descs for a non-tso pkt */ +#define VMXNET3_MAX_TXD_PER_PKT 16 + +/* Max size of a single rx buffer */ +#define VMXNET3_MAX_RX_BUF_SIZE ((1 << 14) - 1) +/* Minimum size of a type 0 buffer */ +#define VMXNET3_MIN_T0_BUF_SIZE 128 +#define VMXNET3_MAX_CSUM_OFFSET 1024 + +/* Ring base address alignment */ +#define VMXNET3_RING_BA_ALIGN 512 +#define VMXNET3_RING_BA_MASK (VMXNET3_RING_BA_ALIGN - 1) + +/* Ring size must be a multiple of 32 */ +#define VMXNET3_RING_SIZE_ALIGN 32 +#define VMXNET3_RING_SIZE_MASK (VMXNET3_RING_SIZE_ALIGN - 1) + +/* Max ring size */ +#define VMXNET3_TX_RING_MAX_SIZE 4096 +#define VMXNET3_TC_RING_MAX_SIZE 4096 +#define VMXNET3_RX_RING_MAX_SIZE 4096 +#define VMXNET3_RC_RING_MAX_SIZE 8192 + +/* a list of reasons for queue stop */ + +enum { + VMXNET3_ERR_NOEOP = 0x80000000, /* cannot find the EOP desc of a pkt */ + VMXNET3_ERR_TXD_REUSE = 0x80000001, /* reuse TxDesc before tx completion */ + VMXNET3_ERR_BIG_PKT = 0x80000002, /* too many TxDesc for a pkt */ + VMXNET3_ERR_DESC_NOT_SPT = 0x80000003, /* descriptor type not supported */ + VMXNET3_ERR_SMALL_BUF = 0x80000004, /* type 0 buffer too small */ + VMXNET3_ERR_STRESS = 0x80000005, /* stress option firing in vmkernel */ + VMXNET3_ERR_SWITCH = 0x80000006, /* mode switch failure */ + VMXNET3_ERR_TXD_INVALID = 0x80000007, /* invalid TxDesc */ +}; + +/* completion descriptor types */ +#define VMXNET3_CDTYPE_TXCOMP 0 /* Tx Completion Descriptor */ +#define VMXNET3_CDTYPE_RXCOMP 3 /* Rx Completion Descriptor */ + +enum { + VMXNET3_GOS_BITS_UNK = 0, /* unknown */ + VMXNET3_GOS_BITS_32 = 1, + VMXNET3_GOS_BITS_64 = 2, +}; + +#define VMXNET3_GOS_TYPE_UNK 0 /* unknown */ +#define VMXNET3_GOS_TYPE_LINUX 1 +#define VMXNET3_GOS_TYPE_WIN 2 +#define VMXNET3_GOS_TYPE_SOLARIS 3 +#define VMXNET3_GOS_TYPE_FREEBSD 4 +#define VMXNET3_GOS_TYPE_PXE 5 + +struct Vmxnet3_GOSInfo { +#ifdef __BIG_ENDIAN_BITFIELD + u32 gosMisc:10; /* other info about gos */ + u32 gosVer:16; /* gos version */ + u32 gosType:4; /* which guest */ + u32 gosBits:2; /* 32-bit or 64-bit? */ +#else + u32 gosBits:2; /* 32-bit or 64-bit? */ + u32 gosType:4; /* which guest */ + u32 gosVer:16; /* gos version */ + u32 gosMisc:10; /* other info about gos */ +#endif /* __BIG_ENDIAN_BITFIELD */ +}; + +struct Vmxnet3_DriverInfo { + __le32 version; + struct Vmxnet3_GOSInfo gos; + __le32 vmxnet3RevSpt; + __le32 uptVerSpt; +}; + + +#define VMXNET3_REV1_MAGIC 0xbabefee1 + +/* + * QueueDescPA must be 128 bytes aligned. It points to an array of + * Vmxnet3_TxQueueDesc followed by an array of Vmxnet3_RxQueueDesc. + * The number of Vmxnet3_TxQueueDesc/Vmxnet3_RxQueueDesc are specified by + * Vmxnet3_MiscConf.numTxQueues/numRxQueues, respectively. + */ +#define VMXNET3_QUEUE_DESC_ALIGN 128 + + +struct Vmxnet3_MiscConf { + struct Vmxnet3_DriverInfo driverInfo; + __le64 uptFeatures; + __le64 ddPA; /* driver data PA */ + __le64 queueDescPA; /* queue descriptor table PA */ + __le32 ddLen; /* driver data len */ + __le32 queueDescLen; /* queue desc. table len in bytes */ + __le32 mtu; + __le16 maxNumRxSG; + u8 numTxQueues; + u8 numRxQueues; + __le32 reserved[4]; +}; + + +struct Vmxnet3_TxQueueConf { + __le64 txRingBasePA; + __le64 dataRingBasePA; + __le64 compRingBasePA; + __le64 ddPA; /* driver data */ + __le64 reserved; + __le32 txRingSize; /* # of tx desc */ + __le32 dataRingSize; /* # of data desc */ + __le32 compRingSize; /* # of comp desc */ + __le32 ddLen; /* size of driver data */ + u8 intrIdx; + u8 _pad[7]; +}; + + +struct Vmxnet3_RxQueueConf { + __le64 rxRingBasePA[2]; + __le64 compRingBasePA; + __le64 ddPA; /* driver data */ + __le64 reserved; + __le32 rxRingSize[2]; /* # of rx desc */ + __le32 compRingSize; /* # of rx comp desc */ + __le32 ddLen; /* size of driver data */ + u8 intrIdx; + u8 _pad[7]; +}; + + +enum vmxnet3_intr_mask_mode { + VMXNET3_IMM_AUTO = 0, + VMXNET3_IMM_ACTIVE = 1, + VMXNET3_IMM_LAZY = 2 +}; + +enum vmxnet3_intr_type { + VMXNET3_IT_AUTO = 0, + VMXNET3_IT_INTX = 1, + VMXNET3_IT_MSI = 2, + VMXNET3_IT_MSIX = 3 +}; + +#define VMXNET3_MAX_TX_QUEUES 8 +#define VMXNET3_MAX_RX_QUEUES 16 +/* addition 1 for events */ +#define VMXNET3_MAX_INTRS 25 + +/* value of intrCtrl */ +#define VMXNET3_IC_DISABLE_ALL 0x1 /* bit 0 */ + + +struct Vmxnet3_IntrConf { + bool autoMask; + u8 numIntrs; /* # of interrupts */ + u8 eventIntrIdx; + u8 modLevels[VMXNET3_MAX_INTRS]; /* moderation level for + * each intr */ + __le32 intrCtrl; + __le32 reserved[2]; +}; + +/* one bit per VLAN ID, the size is in the units of u32 */ +#define VMXNET3_VFT_SIZE (4096/(sizeof(uint32_t)*8)) + + +struct Vmxnet3_QueueStatus { + bool stopped; + u8 _pad[3]; + __le32 error; +}; + + +struct Vmxnet3_TxQueueCtrl { + __le32 txNumDeferred; + __le32 txThreshold; + __le64 reserved; +}; + + +struct Vmxnet3_RxQueueCtrl { + bool updateRxProd; + u8 _pad[7]; + __le64 reserved; +}; + +enum { + VMXNET3_RXM_UCAST = 0x01, /* unicast only */ + VMXNET3_RXM_MCAST = 0x02, /* multicast passing the filters */ + VMXNET3_RXM_BCAST = 0x04, /* broadcast only */ + VMXNET3_RXM_ALL_MULTI = 0x08, /* all multicast */ + VMXNET3_RXM_PROMISC = 0x10 /* promiscuous */ +}; + +struct Vmxnet3_RxFilterConf { + __le32 rxMode; /* VMXNET3_RXM_xxx */ + __le16 mfTableLen; /* size of the multicast filter table */ + __le16 _pad1; + __le64 mfTablePA; /* PA of the multicast filters table */ + __le32 vfTable[VMXNET3_VFT_SIZE]; /* vlan filter */ +}; + + +#define VMXNET3_PM_MAX_FILTERS 6 +#define VMXNET3_PM_MAX_PATTERN_SIZE 128 +#define VMXNET3_PM_MAX_MASK_SIZE (VMXNET3_PM_MAX_PATTERN_SIZE / 8) + +#define VMXNET3_PM_WAKEUP_MAGIC cpu_to_le16(0x01) /* wake up on magic pkts */ +#define VMXNET3_PM_WAKEUP_FILTER cpu_to_le16(0x02) /* wake up on pkts matching + * filters */ + + +struct Vmxnet3_PM_PktFilter { + u8 maskSize; + u8 patternSize; + u8 mask[VMXNET3_PM_MAX_MASK_SIZE]; + u8 pattern[VMXNET3_PM_MAX_PATTERN_SIZE]; + u8 pad[6]; +}; + + +struct Vmxnet3_PMConf { + __le16 wakeUpEvents; /* VMXNET3_PM_WAKEUP_xxx */ + u8 numFilters; + u8 pad[5]; + struct Vmxnet3_PM_PktFilter filters[VMXNET3_PM_MAX_FILTERS]; +}; + + +struct Vmxnet3_VariableLenConfDesc { + __le32 confVer; + __le32 confLen; + __le64 confPA; +}; + + +struct Vmxnet3_TxQueueDesc { + struct Vmxnet3_TxQueueCtrl ctrl; + struct Vmxnet3_TxQueueConf conf; + + /* Driver read after a GET command */ + struct Vmxnet3_QueueStatus status; + struct UPT1_TxStats stats; + u8 _pad[88]; /* 128 aligned */ +}; + + +struct Vmxnet3_RxQueueDesc { + struct Vmxnet3_RxQueueCtrl ctrl; + struct Vmxnet3_RxQueueConf conf; + /* Driver read after a GET commad */ + struct Vmxnet3_QueueStatus status; + struct UPT1_RxStats stats; + u8 __pad[88]; /* 128 aligned */ +}; + + +struct Vmxnet3_DSDevRead { + /* read-only region for device, read by dev in response to a SET cmd */ + struct Vmxnet3_MiscConf misc; + struct Vmxnet3_IntrConf intrConf; + struct Vmxnet3_RxFilterConf rxFilterConf; + struct Vmxnet3_VariableLenConfDesc rssConfDesc; + struct Vmxnet3_VariableLenConfDesc pmConfDesc; + struct Vmxnet3_VariableLenConfDesc pluginConfDesc; +}; + +/* All structures in DriverShared are padded to multiples of 8 bytes */ +struct Vmxnet3_DriverShared { + __le32 magic; + /* make devRead start at 64bit boundaries */ + __le32 pad; + struct Vmxnet3_DSDevRead devRead; + __le32 ecr; + __le32 reserved[5]; +}; + + +#define VMXNET3_ECR_RQERR (1 << 0) +#define VMXNET3_ECR_TQERR (1 << 1) +#define VMXNET3_ECR_LINK (1 << 2) +#define VMXNET3_ECR_DIC (1 << 3) +#define VMXNET3_ECR_DEBUG (1 << 4) + +/* flip the gen bit of a ring */ +#define VMXNET3_FLIP_RING_GEN(gen) ((gen) = (gen) ^ 0x1) + +/* only use this if moving the idx won't affect the gen bit */ +#define VMXNET3_INC_RING_IDX_ONLY(idx, ring_size) \ + do {\ + (idx)++;\ + if (unlikely((idx) == (ring_size))) {\ + (idx) = 0;\ + } \ + } while (0) + +#define VMXNET3_SET_VFTABLE_ENTRY(vfTable, vid) \ + (vfTable[vid >> 5] |= (1 << (vid & 31))) +#define VMXNET3_CLEAR_VFTABLE_ENTRY(vfTable, vid) \ + (vfTable[vid >> 5] &= ~(1 << (vid & 31))) + +#define VMXNET3_VFTABLE_ENTRY_IS_SET(vfTable, vid) \ + ((vfTable[vid >> 5] & (1 << (vid & 31))) != 0) + +#define VMXNET3_MAX_MTU 9000 +#define VMXNET3_MIN_MTU 60 + +#define VMXNET3_LINK_UP (10000 << 16 | 1) /* 10 Gbps, up */ +#define VMXNET3_LINK_DOWN 0 + +#undef u64 +#undef u32 +#undef u16 +#undef u8 +#undef __le16 +#undef __le32 +#undef __le64 +#undef __packed +#undef const_cpu_to_le64 +#if defined(HOST_WORDS_BIGENDIAN) +#undef __BIG_ENDIAN_BITFIELD +#endif + +#endif diff --git a/hw/vmxnet_debug.h b/hw/vmxnet_debug.h new file mode 100644 index 0000000000..96dae0f916 --- /dev/null +++ b/hw/vmxnet_debug.h @@ -0,0 +1,115 @@ +/* + * QEMU VMWARE VMXNET* paravirtual NICs - debugging facilities + * + * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) + * + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Dmitry Fleytman <dmitry@daynix.com> + * Tamir Shomer <tamirs@daynix.com> + * Yan Vugenfirer <yan@daynix.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef _QEMU_VMXNET_DEBUG_H +#define _QEMU_VMXNET_DEBUG_H + +#define VMXNET_DEVICE_NAME "vmxnet3" + +/* #define VMXNET_DEBUG_CB */ +#define VMXNET_DEBUG_WARNINGS +#define VMXNET_DEBUG_ERRORS +/* #define VMXNET_DEBUG_INTERRUPTS */ +/* #define VMXNET_DEBUG_CONFIG */ +/* #define VMXNET_DEBUG_RINGS */ +/* #define VMXNET_DEBUG_PACKETS */ +/* #define VMXNET_DEBUG_SHMEM_ACCESS */ + +#ifdef VMXNET_DEBUG_SHMEM_ACCESS +#define VMW_SHPRN(fmt, ...) \ + do { \ + printf("[%s][SH][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } while (0) +#else +#define VMW_SHPRN(fmt, ...) do {} while (0) +#endif + +#ifdef VMXNET_DEBUG_CB +#define VMW_CBPRN(fmt, ...) \ + do { \ + printf("[%s][CB][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } while (0) +#else +#define VMW_CBPRN(fmt, ...) do {} while (0) +#endif + +#ifdef VMXNET_DEBUG_PACKETS +#define VMW_PKPRN(fmt, ...) \ + do { \ + printf("[%s][PK][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } while (0) +#else +#define VMW_PKPRN(fmt, ...) do {} while (0) +#endif + +#ifdef VMXNET_DEBUG_WARNINGS +#define VMW_WRPRN(fmt, ...) \ + do { \ + printf("[%s][WR][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } while (0) +#else +#define VMW_WRPRN(fmt, ...) do {} while (0) +#endif + +#ifdef VMXNET_DEBUG_ERRORS +#define VMW_ERPRN(fmt, ...) \ + do { \ + printf("[%s][ER][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } while (0) +#else +#define VMW_ERPRN(fmt, ...) do {} while (0) +#endif + +#ifdef VMXNET_DEBUG_INTERRUPTS +#define VMW_IRPRN(fmt, ...) \ + do { \ + printf("[%s][IR][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } while (0) +#else +#define VMW_IRPRN(fmt, ...) do {} while (0) +#endif + +#ifdef VMXNET_DEBUG_CONFIG +#define VMW_CFPRN(fmt, ...) \ + do { \ + printf("[%s][CF][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } while (0) +#else +#define VMW_CFPRN(fmt, ...) do {} while (0) +#endif + +#ifdef VMXNET_DEBUG_RINGS +#define VMW_RIPRN(fmt, ...) \ + do { \ + printf("[%s][RI][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } while (0) +#else +#define VMW_RIPRN(fmt, ...) do {} while (0) +#endif + +#define VMXNET_MF "%02X:%02X:%02X:%02X:%02X:%02X" +#define VMXNET_MA(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] + +#endif /* _QEMU_VMXNET3_DEBUG_H */ diff --git a/hw/vmxnet_rx_pkt.c b/hw/vmxnet_rx_pkt.c new file mode 100644 index 0000000000..a40e346293 --- /dev/null +++ b/hw/vmxnet_rx_pkt.c @@ -0,0 +1,187 @@ +/* + * QEMU VMWARE VMXNET* paravirtual NICs - RX packets abstractions + * + * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) + * + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Dmitry Fleytman <dmitry@daynix.com> + * Tamir Shomer <tamirs@daynix.com> + * Yan Vugenfirer <yan@daynix.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "vmxnet_rx_pkt.h" +#include "net/eth.h" +#include "qemu-common.h" +#include "qemu/iov.h" +#include "net/checksum.h" +#include "net/tap.h" + +/* + * RX packet may contain up to 2 fragments - rebuilt eth header + * in case of VLAN tag stripping + * and payload received from QEMU - in any case + */ +#define VMXNET_MAX_RX_PACKET_FRAGMENTS (2) + +struct VmxnetRxPkt { + struct virtio_net_hdr virt_hdr; + uint8_t ehdr_buf[ETH_MAX_L2_HDR_LEN]; + struct iovec vec[VMXNET_MAX_RX_PACKET_FRAGMENTS]; + uint16_t vec_len; + uint32_t tot_len; + uint16_t tci; + bool vlan_stripped; + bool has_virt_hdr; + eth_pkt_types_e packet_type; + + /* Analysis results */ + bool isip4; + bool isip6; + bool isudp; + bool istcp; +}; + +void vmxnet_rx_pkt_init(struct VmxnetRxPkt **pkt, bool has_virt_hdr) +{ + struct VmxnetRxPkt *p = g_malloc0(sizeof *p); + p->has_virt_hdr = has_virt_hdr; + *pkt = p; +} + +void vmxnet_rx_pkt_uninit(struct VmxnetRxPkt *pkt) +{ + g_free(pkt); +} + +struct virtio_net_hdr *vmxnet_rx_pkt_get_vhdr(struct VmxnetRxPkt *pkt) +{ + assert(pkt); + return &pkt->virt_hdr; +} + +void vmxnet_rx_pkt_attach_data(struct VmxnetRxPkt *pkt, const void *data, + size_t len, bool strip_vlan) +{ + uint16_t tci = 0; + uint16_t ploff; + assert(pkt); + pkt->vlan_stripped = false; + + if (strip_vlan) { + pkt->vlan_stripped = eth_strip_vlan(data, pkt->ehdr_buf, &ploff, &tci); + } + + if (pkt->vlan_stripped) { + pkt->vec[0].iov_base = pkt->ehdr_buf; + pkt->vec[0].iov_len = ploff - sizeof(struct vlan_header); + pkt->vec[1].iov_base = (uint8_t *) data + ploff; + pkt->vec[1].iov_len = len - ploff; + pkt->vec_len = 2; + pkt->tot_len = len - ploff + sizeof(struct eth_header); + } else { + pkt->vec[0].iov_base = (void *)data; + pkt->vec[0].iov_len = len; + pkt->vec_len = 1; + pkt->tot_len = len; + } + + pkt->tci = tci; + + eth_get_protocols(data, len, &pkt->isip4, &pkt->isip6, + &pkt->isudp, &pkt->istcp); +} + +void vmxnet_rx_pkt_dump(struct VmxnetRxPkt *pkt) +{ +#ifdef VMXNET_RX_PKT_DEBUG + VmxnetRxPkt *pkt = (VmxnetRxPkt *)pkt; + assert(pkt); + + printf("RX PKT: tot_len: %d, vlan_stripped: %d, vlan_tag: %d\n", + pkt->tot_len, pkt->vlan_stripped, pkt->tci); +#endif +} + +void vmxnet_rx_pkt_set_packet_type(struct VmxnetRxPkt *pkt, + eth_pkt_types_e packet_type) +{ + assert(pkt); + + pkt->packet_type = packet_type; + +} + +eth_pkt_types_e vmxnet_rx_pkt_get_packet_type(struct VmxnetRxPkt *pkt) +{ + assert(pkt); + + return pkt->packet_type; +} + +size_t vmxnet_rx_pkt_get_total_len(struct VmxnetRxPkt *pkt) +{ + assert(pkt); + + return pkt->tot_len; +} + +void vmxnet_rx_pkt_get_protocols(struct VmxnetRxPkt *pkt, + bool *isip4, bool *isip6, + bool *isudp, bool *istcp) +{ + assert(pkt); + + *isip4 = pkt->isip4; + *isip6 = pkt->isip6; + *isudp = pkt->isudp; + *istcp = pkt->istcp; +} + +struct iovec *vmxnet_rx_pkt_get_iovec(struct VmxnetRxPkt *pkt) +{ + assert(pkt); + + return pkt->vec; +} + +void vmxnet_rx_pkt_set_vhdr(struct VmxnetRxPkt *pkt, + struct virtio_net_hdr *vhdr) +{ + assert(pkt); + + memcpy(&pkt->virt_hdr, vhdr, sizeof pkt->virt_hdr); +} + +bool vmxnet_rx_pkt_is_vlan_stripped(struct VmxnetRxPkt *pkt) +{ + assert(pkt); + + return pkt->vlan_stripped; +} + +bool vmxnet_rx_pkt_has_virt_hdr(struct VmxnetRxPkt *pkt) +{ + assert(pkt); + + return pkt->has_virt_hdr; +} + +uint16_t vmxnet_rx_pkt_get_num_frags(struct VmxnetRxPkt *pkt) +{ + assert(pkt); + + return pkt->vec_len; +} + +uint16_t vmxnet_rx_pkt_get_vlan_tag(struct VmxnetRxPkt *pkt) +{ + assert(pkt); + + return pkt->tci; +} diff --git a/hw/vmxnet_rx_pkt.h b/hw/vmxnet_rx_pkt.h new file mode 100644 index 0000000000..6b2c60ef10 --- /dev/null +++ b/hw/vmxnet_rx_pkt.h @@ -0,0 +1,174 @@ +/* + * QEMU VMWARE VMXNET* paravirtual NICs - RX packets abstraction + * + * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) + * + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Dmitry Fleytman <dmitry@daynix.com> + * Tamir Shomer <tamirs@daynix.com> + * Yan Vugenfirer <yan@daynix.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef VMXNET_RX_PKT_H +#define VMXNET_RX_PKT_H + +#include "stdint.h" +#include "stdbool.h" +#include "net/eth.h" + +/* defines to enable packet dump functions */ +/*#define VMXNET_RX_PKT_DEBUG*/ + +struct VmxnetRxPkt; + +/** + * Clean all rx packet resources + * + * @pkt: packet + * + */ +void vmxnet_rx_pkt_uninit(struct VmxnetRxPkt *pkt); + +/** + * Init function for rx packet functionality + * + * @pkt: packet pointer + * @has_virt_hdr: device uses virtio header + * + */ +void vmxnet_rx_pkt_init(struct VmxnetRxPkt **pkt, bool has_virt_hdr); + +/** + * returns total length of data attached to rx context + * + * @pkt: packet + * + * Return: nothing + * + */ +size_t vmxnet_rx_pkt_get_total_len(struct VmxnetRxPkt *pkt); + +/** + * fetches packet analysis results + * + * @pkt: packet + * @isip4: whether the packet given is IPv4 + * @isip6: whether the packet given is IPv6 + * @isudp: whether the packet given is UDP + * @istcp: whether the packet given is TCP + * + */ +void vmxnet_rx_pkt_get_protocols(struct VmxnetRxPkt *pkt, + bool *isip4, bool *isip6, + bool *isudp, bool *istcp); + +/** + * returns virtio header stored in rx context + * + * @pkt: packet + * @ret: virtio header + * + */ +struct virtio_net_hdr *vmxnet_rx_pkt_get_vhdr(struct VmxnetRxPkt *pkt); + +/** + * returns packet type + * + * @pkt: packet + * @ret: packet type + * + */ +eth_pkt_types_e vmxnet_rx_pkt_get_packet_type(struct VmxnetRxPkt *pkt); + +/** + * returns vlan tag + * + * @pkt: packet + * @ret: VLAN tag + * + */ +uint16_t vmxnet_rx_pkt_get_vlan_tag(struct VmxnetRxPkt *pkt); + +/** + * tells whether vlan was stripped from the packet + * + * @pkt: packet + * @ret: VLAN stripped sign + * + */ +bool vmxnet_rx_pkt_is_vlan_stripped(struct VmxnetRxPkt *pkt); + +/** + * notifies caller if the packet has virtio header + * + * @pkt: packet + * @ret: true if packet has virtio header, false otherwize + * + */ +bool vmxnet_rx_pkt_has_virt_hdr(struct VmxnetRxPkt *pkt); + +/** + * returns number of frags attached to the packet + * + * @pkt: packet + * @ret: number of frags + * + */ +uint16_t vmxnet_rx_pkt_get_num_frags(struct VmxnetRxPkt *pkt); + +/** + * attach data to rx packet + * + * @pkt: packet + * @data: pointer to the data buffer + * @len: data length + * @strip_vlan: should the module strip vlan from data + * + */ +void vmxnet_rx_pkt_attach_data(struct VmxnetRxPkt *pkt, const void *data, + size_t len, bool strip_vlan); + +/** + * returns io vector that holds the attached data + * + * @pkt: packet + * @ret: pointer to IOVec + * + */ +struct iovec *vmxnet_rx_pkt_get_iovec(struct VmxnetRxPkt *pkt); + +/** + * prints rx packet data if debug is enabled + * + * @pkt: packet + * + */ +void vmxnet_rx_pkt_dump(struct VmxnetRxPkt *pkt); + +/** + * copy passed vhdr data to packet context + * + * @pkt: packet + * @vhdr: VHDR buffer + * + */ +void vmxnet_rx_pkt_set_vhdr(struct VmxnetRxPkt *pkt, + struct virtio_net_hdr *vhdr); + +/** + * save packet type in packet context + * + * @pkt: packet + * @packet_type: the packet type + * + */ +void vmxnet_rx_pkt_set_packet_type(struct VmxnetRxPkt *pkt, + eth_pkt_types_e packet_type); + +#endif diff --git a/hw/vmxnet_tx_pkt.c b/hw/vmxnet_tx_pkt.c new file mode 100644 index 0000000000..b1e795b3b2 --- /dev/null +++ b/hw/vmxnet_tx_pkt.c @@ -0,0 +1,567 @@ +/* + * QEMU VMWARE VMXNET* paravirtual NICs - TX packets abstractions + * + * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) + * + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Dmitry Fleytman <dmitry@daynix.com> + * Tamir Shomer <tamirs@daynix.com> + * Yan Vugenfirer <yan@daynix.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "vmxnet_tx_pkt.h" +#include "net/eth.h" +#include "qemu-common.h" +#include "qemu/iov.h" +#include "net/checksum.h" +#include "net/tap.h" +#include "net/net.h" +#include "exec/cpu-common.h" + +enum { + VMXNET_TX_PKT_VHDR_FRAG = 0, + VMXNET_TX_PKT_L2HDR_FRAG, + VMXNET_TX_PKT_L3HDR_FRAG, + VMXNET_TX_PKT_PL_START_FRAG +}; + +/* TX packet private context */ +struct VmxnetTxPkt { + struct virtio_net_hdr virt_hdr; + bool has_virt_hdr; + + struct iovec *raw; + uint32_t raw_frags; + uint32_t max_raw_frags; + + struct iovec *vec; + + uint8_t l2_hdr[ETH_MAX_L2_HDR_LEN]; + + uint32_t payload_len; + + uint32_t payload_frags; + uint32_t max_payload_frags; + + uint16_t hdr_len; + eth_pkt_types_e packet_type; + uint8_t l4proto; +}; + +void vmxnet_tx_pkt_init(struct VmxnetTxPkt **pkt, uint32_t max_frags, + bool has_virt_hdr) +{ + struct VmxnetTxPkt *p = g_malloc0(sizeof *p); + + p->vec = g_malloc((sizeof *p->vec) * + (max_frags + VMXNET_TX_PKT_PL_START_FRAG)); + + p->raw = g_malloc((sizeof *p->raw) * max_frags); + + p->max_payload_frags = max_frags; + p->max_raw_frags = max_frags; + p->has_virt_hdr = has_virt_hdr; + p->vec[VMXNET_TX_PKT_VHDR_FRAG].iov_base = &p->virt_hdr; + p->vec[VMXNET_TX_PKT_VHDR_FRAG].iov_len = + p->has_virt_hdr ? sizeof p->virt_hdr : 0; + p->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_base = &p->l2_hdr; + p->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base = NULL; + p->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len = 0; + + *pkt = p; +} + +void vmxnet_tx_pkt_uninit(struct VmxnetTxPkt *pkt) +{ + if (pkt) { + g_free(pkt->vec); + g_free(pkt->raw); + g_free(pkt); + } +} + +void vmxnet_tx_pkt_update_ip_checksums(struct VmxnetTxPkt *pkt) +{ + uint16_t csum; + uint32_t ph_raw_csum; + assert(pkt); + uint8_t gso_type = pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN; + struct ip_header *ip_hdr; + + if (VIRTIO_NET_HDR_GSO_TCPV4 != gso_type && + VIRTIO_NET_HDR_GSO_UDP != gso_type) { + return; + } + + ip_hdr = pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base; + + if (pkt->payload_len + pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len > + ETH_MAX_IP_DGRAM_LEN) { + return; + } + + ip_hdr->ip_len = cpu_to_be16(pkt->payload_len + + pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len); + + /* Calculate IP header checksum */ + ip_hdr->ip_sum = 0; + csum = net_raw_checksum((uint8_t *)ip_hdr, + pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len); + ip_hdr->ip_sum = cpu_to_be16(csum); + + /* Calculate IP pseudo header checksum */ + ph_raw_csum = eth_calc_pseudo_hdr_csum(ip_hdr, pkt->payload_len); + csum = cpu_to_be16(~net_checksum_finish(ph_raw_csum)); + iov_from_buf(&pkt->vec[VMXNET_TX_PKT_PL_START_FRAG], pkt->payload_frags, + pkt->virt_hdr.csum_offset, &csum, sizeof(csum)); +} + +static void vmxnet_tx_pkt_calculate_hdr_len(struct VmxnetTxPkt *pkt) +{ + pkt->hdr_len = pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len + + pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len; +} + +static bool vmxnet_tx_pkt_parse_headers(struct VmxnetTxPkt *pkt) +{ + struct iovec *l2_hdr, *l3_hdr; + size_t bytes_read; + size_t full_ip6hdr_len; + uint16_t l3_proto; + + assert(pkt); + + l2_hdr = &pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG]; + l3_hdr = &pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG]; + + bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, 0, l2_hdr->iov_base, + ETH_MAX_L2_HDR_LEN); + if (bytes_read < ETH_MAX_L2_HDR_LEN) { + l2_hdr->iov_len = 0; + return false; + } else { + l2_hdr->iov_len = eth_get_l2_hdr_length(l2_hdr->iov_base); + } + + l3_proto = eth_get_l3_proto(l2_hdr->iov_base, l2_hdr->iov_len); + + switch (l3_proto) { + case ETH_P_IP: + l3_hdr->iov_base = g_malloc(ETH_MAX_IP4_HDR_LEN); + + bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, l2_hdr->iov_len, + l3_hdr->iov_base, sizeof(struct ip_header)); + + if (bytes_read < sizeof(struct ip_header)) { + l3_hdr->iov_len = 0; + return false; + } + + l3_hdr->iov_len = IP_HDR_GET_LEN(l3_hdr->iov_base); + pkt->l4proto = ((struct ip_header *) l3_hdr->iov_base)->ip_p; + + /* copy optional IPv4 header data */ + bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, + l2_hdr->iov_len + sizeof(struct ip_header), + l3_hdr->iov_base + sizeof(struct ip_header), + l3_hdr->iov_len - sizeof(struct ip_header)); + if (bytes_read < l3_hdr->iov_len - sizeof(struct ip_header)) { + l3_hdr->iov_len = 0; + return false; + } + break; + + case ETH_P_IPV6: + if (!eth_parse_ipv6_hdr(pkt->raw, pkt->raw_frags, l2_hdr->iov_len, + &pkt->l4proto, &full_ip6hdr_len)) { + l3_hdr->iov_len = 0; + return false; + } + + l3_hdr->iov_base = g_malloc(full_ip6hdr_len); + + bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, l2_hdr->iov_len, + l3_hdr->iov_base, full_ip6hdr_len); + + if (bytes_read < full_ip6hdr_len) { + l3_hdr->iov_len = 0; + return false; + } else { + l3_hdr->iov_len = full_ip6hdr_len; + } + break; + + default: + l3_hdr->iov_len = 0; + break; + } + + vmxnet_tx_pkt_calculate_hdr_len(pkt); + pkt->packet_type = get_eth_packet_type(l2_hdr->iov_base); + return true; +} + +static bool vmxnet_tx_pkt_rebuild_payload(struct VmxnetTxPkt *pkt) +{ + size_t payload_len = iov_size(pkt->raw, pkt->raw_frags) - pkt->hdr_len; + + pkt->payload_frags = iov_copy(&pkt->vec[VMXNET_TX_PKT_PL_START_FRAG], + pkt->max_payload_frags, + pkt->raw, pkt->raw_frags, + pkt->hdr_len, payload_len); + + if (pkt->payload_frags != (uint32_t) -1) { + pkt->payload_len = payload_len; + return true; + } else { + return false; + } +} + +bool vmxnet_tx_pkt_parse(struct VmxnetTxPkt *pkt) +{ + return vmxnet_tx_pkt_parse_headers(pkt) && + vmxnet_tx_pkt_rebuild_payload(pkt); +} + +struct virtio_net_hdr *vmxnet_tx_pkt_get_vhdr(struct VmxnetTxPkt *pkt) +{ + assert(pkt); + return &pkt->virt_hdr; +} + +static uint8_t vmxnet_tx_pkt_get_gso_type(struct VmxnetTxPkt *pkt, + bool tso_enable) +{ + uint8_t rc = VIRTIO_NET_HDR_GSO_NONE; + uint16_t l3_proto; + + l3_proto = eth_get_l3_proto(pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_base, + pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len); + + if (!tso_enable) { + goto func_exit; + } + + rc = eth_get_gso_type(l3_proto, pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base, + pkt->l4proto); + +func_exit: + return rc; +} + +void vmxnet_tx_pkt_build_vheader(struct VmxnetTxPkt *pkt, bool tso_enable, + bool csum_enable, uint32_t gso_size) +{ + struct tcp_hdr l4hdr; + assert(pkt); + + /* csum has to be enabled if tso is. */ + assert(csum_enable || !tso_enable); + + pkt->virt_hdr.gso_type = vmxnet_tx_pkt_get_gso_type(pkt, tso_enable); + + switch (pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { + case VIRTIO_NET_HDR_GSO_NONE: + pkt->virt_hdr.hdr_len = 0; + pkt->virt_hdr.gso_size = 0; + break; + + case VIRTIO_NET_HDR_GSO_UDP: + pkt->virt_hdr.gso_size = IP_FRAG_ALIGN_SIZE(gso_size); + pkt->virt_hdr.hdr_len = pkt->hdr_len + sizeof(struct udp_header); + break; + + case VIRTIO_NET_HDR_GSO_TCPV4: + case VIRTIO_NET_HDR_GSO_TCPV6: + iov_to_buf(&pkt->vec[VMXNET_TX_PKT_PL_START_FRAG], pkt->payload_frags, + 0, &l4hdr, sizeof(l4hdr)); + pkt->virt_hdr.hdr_len = pkt->hdr_len + l4hdr.th_off * sizeof(uint32_t); + pkt->virt_hdr.gso_size = IP_FRAG_ALIGN_SIZE(gso_size); + break; + + default: + assert(false); + } + + if (csum_enable) { + switch (pkt->l4proto) { + case IP_PROTO_TCP: + pkt->virt_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; + pkt->virt_hdr.csum_start = pkt->hdr_len; + pkt->virt_hdr.csum_offset = offsetof(struct tcp_hdr, th_sum); + break; + case IP_PROTO_UDP: + pkt->virt_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; + pkt->virt_hdr.csum_start = pkt->hdr_len; + pkt->virt_hdr.csum_offset = offsetof(struct udp_hdr, uh_sum); + break; + default: + break; + } + } +} + +void vmxnet_tx_pkt_setup_vlan_header(struct VmxnetTxPkt *pkt, uint16_t vlan) +{ + bool is_new; + assert(pkt); + + eth_setup_vlan_headers(pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_base, + vlan, &is_new); + + /* update l2hdrlen */ + if (is_new) { + pkt->hdr_len += sizeof(struct vlan_header); + pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len += + sizeof(struct vlan_header); + } +} + +bool vmxnet_tx_pkt_add_raw_fragment(struct VmxnetTxPkt *pkt, hwaddr pa, + size_t len) +{ + hwaddr mapped_len = 0; + struct iovec *ventry; + assert(pkt); + assert(pkt->max_raw_frags > pkt->raw_frags); + + if (!len) { + return true; + } + + ventry = &pkt->raw[pkt->raw_frags]; + mapped_len = len; + + ventry->iov_base = cpu_physical_memory_map(pa, &mapped_len, false); + ventry->iov_len = mapped_len; + pkt->raw_frags += !!ventry->iov_base; + + if ((ventry->iov_base == NULL) || (len != mapped_len)) { + return false; + } + + return true; +} + +eth_pkt_types_e vmxnet_tx_pkt_get_packet_type(struct VmxnetTxPkt *pkt) +{ + assert(pkt); + + return pkt->packet_type; +} + +size_t vmxnet_tx_pkt_get_total_len(struct VmxnetTxPkt *pkt) +{ + assert(pkt); + + return pkt->hdr_len + pkt->payload_len; +} + +void vmxnet_tx_pkt_dump(struct VmxnetTxPkt *pkt) +{ +#ifdef VMXNET_TX_PKT_DEBUG + assert(pkt); + + printf("TX PKT: hdr_len: %d, pkt_type: 0x%X, l2hdr_len: %lu, " + "l3hdr_len: %lu, payload_len: %u\n", pkt->hdr_len, pkt->packet_type, + pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len, + pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len, pkt->payload_len); +#endif +} + +void vmxnet_tx_pkt_reset(struct VmxnetTxPkt *pkt) +{ + int i; + + /* no assert, as reset can be called before tx_pkt_init */ + if (!pkt) { + return; + } + + memset(&pkt->virt_hdr, 0, sizeof(pkt->virt_hdr)); + + g_free(pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base); + pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base = NULL; + + assert(pkt->vec); + for (i = VMXNET_TX_PKT_L2HDR_FRAG; + i < pkt->payload_frags + VMXNET_TX_PKT_PL_START_FRAG; i++) { + pkt->vec[i].iov_len = 0; + } + pkt->payload_len = 0; + pkt->payload_frags = 0; + + assert(pkt->raw); + for (i = 0; i < pkt->raw_frags; i++) { + assert(pkt->raw[i].iov_base); + cpu_physical_memory_unmap(pkt->raw[i].iov_base, pkt->raw[i].iov_len, + false, pkt->raw[i].iov_len); + pkt->raw[i].iov_len = 0; + } + pkt->raw_frags = 0; + + pkt->hdr_len = 0; + pkt->packet_type = 0; + pkt->l4proto = 0; +} + +static void vmxnet_tx_pkt_do_sw_csum(struct VmxnetTxPkt *pkt) +{ + struct iovec *iov = &pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG]; + uint32_t csum_cntr; + uint16_t csum = 0; + /* num of iovec without vhdr */ + uint32_t iov_len = pkt->payload_frags + VMXNET_TX_PKT_PL_START_FRAG - 1; + uint16_t csl; + struct ip_header *iphdr; + size_t csum_offset = pkt->virt_hdr.csum_start + pkt->virt_hdr.csum_offset; + + /* Put zero to checksum field */ + iov_from_buf(iov, iov_len, csum_offset, &csum, sizeof csum); + + /* Calculate L4 TCP/UDP checksum */ + csl = pkt->payload_len; + + /* data checksum */ + csum_cntr = + net_checksum_add_iov(iov, iov_len, pkt->virt_hdr.csum_start, csl); + /* add pseudo header to csum */ + iphdr = pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base; + csum_cntr += eth_calc_pseudo_hdr_csum(iphdr, csl); + + /* Put the checksum obtained into the packet */ + csum = cpu_to_be16(net_checksum_finish(csum_cntr)); + iov_from_buf(iov, iov_len, csum_offset, &csum, sizeof csum); +} + +enum { + VMXNET_TX_PKT_FRAGMENT_L2_HDR_POS = 0, + VMXNET_TX_PKT_FRAGMENT_L3_HDR_POS, + VMXNET_TX_PKT_FRAGMENT_HEADER_NUM +}; + +#define VMXNET_MAX_FRAG_SG_LIST (64) + +static size_t vmxnet_tx_pkt_fetch_fragment(struct VmxnetTxPkt *pkt, + int *src_idx, size_t *src_offset, struct iovec *dst, int *dst_idx) +{ + size_t fetched = 0; + struct iovec *src = pkt->vec; + + *dst_idx = VMXNET_TX_PKT_FRAGMENT_HEADER_NUM; + + while (fetched < pkt->virt_hdr.gso_size) { + + /* no more place in fragment iov */ + if (*dst_idx == VMXNET_MAX_FRAG_SG_LIST) { + break; + } + + /* no more data in iovec */ + if (*src_idx == (pkt->payload_frags + VMXNET_TX_PKT_PL_START_FRAG)) { + break; + } + + + dst[*dst_idx].iov_base = src[*src_idx].iov_base + *src_offset; + dst[*dst_idx].iov_len = MIN(src[*src_idx].iov_len - *src_offset, + pkt->virt_hdr.gso_size - fetched); + + *src_offset += dst[*dst_idx].iov_len; + fetched += dst[*dst_idx].iov_len; + + if (*src_offset == src[*src_idx].iov_len) { + *src_offset = 0; + (*src_idx)++; + } + + (*dst_idx)++; + } + + return fetched; +} + +static bool vmxnet_tx_pkt_do_sw_fragmentation(struct VmxnetTxPkt *pkt, + NetClientState *nc) +{ + struct iovec fragment[VMXNET_MAX_FRAG_SG_LIST]; + size_t fragment_len = 0; + bool more_frags = false; + + /* some pointers for shorter code */ + void *l2_iov_base, *l3_iov_base; + size_t l2_iov_len, l3_iov_len; + int src_idx = VMXNET_TX_PKT_PL_START_FRAG, dst_idx; + size_t src_offset = 0; + size_t fragment_offset = 0; + + l2_iov_base = pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_base; + l2_iov_len = pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len; + l3_iov_base = pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base; + l3_iov_len = pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len; + + /* Copy headers */ + fragment[VMXNET_TX_PKT_FRAGMENT_L2_HDR_POS].iov_base = l2_iov_base; + fragment[VMXNET_TX_PKT_FRAGMENT_L2_HDR_POS].iov_len = l2_iov_len; + fragment[VMXNET_TX_PKT_FRAGMENT_L3_HDR_POS].iov_base = l3_iov_base; + fragment[VMXNET_TX_PKT_FRAGMENT_L3_HDR_POS].iov_len = l3_iov_len; + + + /* Put as much data as possible and send */ + do { + fragment_len = vmxnet_tx_pkt_fetch_fragment(pkt, &src_idx, &src_offset, + fragment, &dst_idx); + + more_frags = (fragment_offset + fragment_len < pkt->payload_len); + + eth_setup_ip4_fragmentation(l2_iov_base, l2_iov_len, l3_iov_base, + l3_iov_len, fragment_len, fragment_offset, more_frags); + + eth_fix_ip4_checksum(l3_iov_base, l3_iov_len); + + qemu_sendv_packet(nc, fragment, dst_idx); + + fragment_offset += fragment_len; + + } while (more_frags); + + return true; +} + +bool vmxnet_tx_pkt_send(struct VmxnetTxPkt *pkt, NetClientState *nc) +{ + assert(pkt); + + if (!pkt->has_virt_hdr && + pkt->virt_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { + vmxnet_tx_pkt_do_sw_csum(pkt); + } + + /* + * Since underlying infrastructure does not support IP datagrams longer + * than 64K we should drop such packets and don't even try to send + */ + if (VIRTIO_NET_HDR_GSO_NONE != pkt->virt_hdr.gso_type) { + if (pkt->payload_len > + ETH_MAX_IP_DGRAM_LEN - + pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len) { + return false; + } + } + + if (pkt->has_virt_hdr || + pkt->virt_hdr.gso_type == VIRTIO_NET_HDR_GSO_NONE) { + qemu_sendv_packet(nc, pkt->vec, + pkt->payload_frags + VMXNET_TX_PKT_PL_START_FRAG); + return true; + } + + return vmxnet_tx_pkt_do_sw_fragmentation(pkt, nc); +} diff --git a/hw/vmxnet_tx_pkt.h b/hw/vmxnet_tx_pkt.h new file mode 100644 index 0000000000..57121a6fe5 --- /dev/null +++ b/hw/vmxnet_tx_pkt.h @@ -0,0 +1,148 @@ +/* + * QEMU VMWARE VMXNET* paravirtual NICs - TX packets abstraction + * + * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) + * + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Dmitry Fleytman <dmitry@daynix.com> + * Tamir Shomer <tamirs@daynix.com> + * Yan Vugenfirer <yan@daynix.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef VMXNET_TX_PKT_H +#define VMXNET_TX_PKT_H + +#include "stdint.h" +#include "stdbool.h" +#include "net/eth.h" +#include "exec/hwaddr.h" + +/* define to enable packet dump functions */ +/*#define VMXNET_TX_PKT_DEBUG*/ + +struct VmxnetTxPkt; + +/** + * Init function for tx packet functionality + * + * @pkt: packet pointer + * @max_frags: max tx ip fragments + * @has_virt_hdr: device uses virtio header. + */ +void vmxnet_tx_pkt_init(struct VmxnetTxPkt **pkt, uint32_t max_frags, + bool has_virt_hdr); + +/** + * Clean all tx packet resources. + * + * @pkt: packet. + */ +void vmxnet_tx_pkt_uninit(struct VmxnetTxPkt *pkt); + +/** + * get virtio header + * + * @pkt: packet + * @ret: virtio header + */ +struct virtio_net_hdr *vmxnet_tx_pkt_get_vhdr(struct VmxnetTxPkt *pkt); + +/** + * build virtio header (will be stored in module context) + * + * @pkt: packet + * @tso_enable: TSO enabled + * @csum_enable: CSO enabled + * @gso_size: MSS size for TSO + * + */ +void vmxnet_tx_pkt_build_vheader(struct VmxnetTxPkt *pkt, bool tso_enable, + bool csum_enable, uint32_t gso_size); + +/** + * updates vlan tag, and adds vlan header in case it is missing + * + * @pkt: packet + * @vlan: VLAN tag + * + */ +void vmxnet_tx_pkt_setup_vlan_header(struct VmxnetTxPkt *pkt, uint16_t vlan); + +/** + * populate data fragment into pkt context. + * + * @pkt: packet + * @pa: physical address of fragment + * @len: length of fragment + * + */ +bool vmxnet_tx_pkt_add_raw_fragment(struct VmxnetTxPkt *pkt, hwaddr pa, + size_t len); + +/** + * fix ip header fields and calculate checksums needed. + * + * @pkt: packet + * + */ +void vmxnet_tx_pkt_update_ip_checksums(struct VmxnetTxPkt *pkt); + +/** + * get length of all populated data. + * + * @pkt: packet + * @ret: total data length + * + */ +size_t vmxnet_tx_pkt_get_total_len(struct VmxnetTxPkt *pkt); + +/** + * get packet type + * + * @pkt: packet + * @ret: packet type + * + */ +eth_pkt_types_e vmxnet_tx_pkt_get_packet_type(struct VmxnetTxPkt *pkt); + +/** + * prints packet data if debug is enabled + * + * @pkt: packet + * + */ +void vmxnet_tx_pkt_dump(struct VmxnetTxPkt *pkt); + +/** + * reset tx packet private context (needed to be called between packets) + * + * @pkt: packet + * + */ +void vmxnet_tx_pkt_reset(struct VmxnetTxPkt *pkt); + +/** + * Send packet to qemu. handles sw offloads if vhdr is not supported. + * + * @pkt: packet + * @nc: NetClientState + * @ret: operation result + * + */ +bool vmxnet_tx_pkt_send(struct VmxnetTxPkt *pkt, NetClientState *nc); + +/** + * parse raw packet data and analyze offload requirements. + * + * @pkt: packet + * + */ +bool vmxnet_tx_pkt_parse(struct VmxnetTxPkt *pkt); + +#endif diff --git a/hw/xenfb.c b/hw/xenfb.c index 3462ded619..7c46a2fa1e 100644 --- a/hw/xenfb.c +++ b/hw/xenfb.c @@ -54,7 +54,7 @@ struct common { struct XenDevice xendev; /* must be first */ void *page; - DisplayState *ds; + QemuConsole *con; }; struct XenInput { @@ -318,8 +318,9 @@ static void xenfb_mouse_event(void *opaque, int dx, int dy, int dz, int button_state) { struct XenInput *xenfb = opaque; - int dw = ds_get_width(xenfb->c.ds); - int dh = ds_get_height(xenfb->c.ds); + DisplaySurface *surface = qemu_console_surface(xenfb->c.con); + int dw = surface_width(surface); + int dh = surface_height(surface); int i; if (xenfb->abs_pointer_wanted) @@ -353,16 +354,9 @@ static int input_initialise(struct XenDevice *xendev) struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); int rc; - if (!in->c.ds) { - char *vfb = xenstore_read_str(NULL, "device/vfb"); - if (vfb == NULL) { - /* there is no vfb, run vkbd on its own */ - in->c.ds = get_displaystate(); - } else { - g_free(vfb); - xen_be_printf(xendev, 1, "ds not set (yet)\n"); - return -1; - } + if (!in->c.con) { + xen_be_printf(xendev, 1, "ds not set (yet)\n"); + return -1; } rc = common_bind(&in->c); @@ -615,12 +609,13 @@ static int xenfb_configure_fb(struct XenFB *xenfb, size_t fb_len_lim, */ static void xenfb_guest_copy(struct XenFB *xenfb, int x, int y, int w, int h) { + DisplaySurface *surface = qemu_console_surface(xenfb->c.con); int line, oops = 0; - int bpp = ds_get_bits_per_pixel(xenfb->c.ds); - int linesize = ds_get_linesize(xenfb->c.ds); - uint8_t *data = ds_get_data(xenfb->c.ds); + int bpp = surface_bits_per_pixel(surface); + int linesize = surface_stride(surface); + uint8_t *data = surface_data(surface); - if (!is_buffer_shared(xenfb->c.ds->surface)) { + if (!is_buffer_shared(surface)) { switch (xenfb->depth) { case 8: if (bpp == 16) { @@ -648,10 +643,10 @@ static void xenfb_guest_copy(struct XenFB *xenfb, int x, int y, int w, int h) xen_be_printf(&xenfb->c.xendev, 0, "%s: oops: convert %d -> %d bpp?\n", __FUNCTION__, xenfb->depth, bpp); - dpy_gfx_update(xenfb->c.ds, x, y, w, h); + dpy_gfx_update(xenfb->c.con, x, y, w, h); } -#ifdef XENFB_TYPE_REFRESH_PERIOD +#if 0 /* def XENFB_TYPE_REFRESH_PERIOD */ static int xenfb_queue_full(struct XenFB *xenfb) { struct xenfb_page *page = xenfb->c.page; @@ -703,13 +698,14 @@ static void xenfb_send_refresh_period(struct XenFB *xenfb, int period) static void xenfb_update(void *opaque) { struct XenFB *xenfb = opaque; + DisplaySurface *surface; int i; if (xenfb->c.xendev.be_state != XenbusStateConnected) return; if (xenfb->feature_update) { -#ifdef XENFB_TYPE_REFRESH_PERIOD +#if 0 /* XENFB_TYPE_REFRESH_PERIOD */ struct DisplayChangeListener *l; int period = 99999999; int idle = 1; @@ -753,21 +749,20 @@ static void xenfb_update(void *opaque) case 16: case 32: /* console.c supported depth -> buffer can be used directly */ - qemu_free_displaysurface(xenfb->c.ds); - xenfb->c.ds->surface = qemu_create_displaysurface_from + surface = qemu_create_displaysurface_from (xenfb->width, xenfb->height, xenfb->depth, xenfb->row_stride, xenfb->pixels + xenfb->offset, false); break; default: /* we must convert stuff */ - qemu_resize_displaysurface(xenfb->c.ds, xenfb->width, xenfb->height); + surface = qemu_create_displaysurface(xenfb->width, xenfb->height); break; } + dpy_gfx_replace_surface(xenfb->c.con, surface); xen_be_printf(&xenfb->c.xendev, 1, "update: resizing: %dx%d @ %d bpp%s\n", xenfb->width, xenfb->height, xenfb->depth, - is_buffer_shared(xenfb->c.ds->surface) ? " (shared)" : ""); - dpy_gfx_resize(xenfb->c.ds); + is_buffer_shared(surface) ? " (shared)" : ""); xenfb->up_fullscreen = 1; } @@ -1009,16 +1004,16 @@ wait_more: /* vfb */ fb = container_of(xfb, struct XenFB, c.xendev); - fb->c.ds = graphic_console_init(xenfb_update, - xenfb_invalidate, - NULL, - NULL, - fb); + fb->c.con = graphic_console_init(xenfb_update, + xenfb_invalidate, + NULL, + NULL, + fb); fb->have_console = 1; /* vkbd */ in = container_of(xin, struct XenInput, c.xendev); - in->c.ds = fb->c.ds; + in->c.con = fb->c.con; /* retry ->init() */ xen_be_check_state(xin); @@ -35,6 +35,7 @@ struct icp_state; qemu_irq xics_get_qirq(struct icp_state *icp, int irq); void xics_set_irq_type(struct icp_state *icp, int irq, bool lsi); -struct icp_state *xics_system_init(int nr_irqs); +struct icp_state *xics_system_init(int nr_servers, int nr_irqs); +void xics_cpu_setup(struct icp_state *icp, PowerPCCPU *cpu); #endif /* __XICS_H__ */ diff --git a/hw/xilinx_spips.c b/hw/xilinx_spips.c index 6c21b9668b..b2397f4a42 100644 --- a/hw/xilinx_spips.c +++ b/hw/xilinx_spips.c @@ -115,6 +115,19 @@ #define SNOOP_NONE 0xFE #define SNOOP_STRIPING 0 +typedef enum { + READ = 0x3, + FAST_READ = 0xb, + DOR = 0x3b, + QOR = 0x6b, + DIOR = 0xbb, + QIOR = 0xeb, + + PP = 0x2, + DPP = 0xa2, + QPP = 0x32, +} FlashCMD; + typedef struct { SysBusDevice busdev; MemoryRegion iomem; @@ -141,10 +154,15 @@ typedef struct { hwaddr lqspi_cached_addr; } XilinxSPIPS; +#define TYPE_XILINX_SPIPS "xilinx,spips" + +#define XILINX_SPIPS(obj) \ + OBJECT_CHECK(XilinxSPIPS, (obj), TYPE_XILINX_SPIPS) + static inline int num_effective_busses(XilinxSPIPS *s) { - return (s->regs[R_LQSPI_STS] & LQSPI_CFG_SEP_BUS && - s->regs[R_LQSPI_STS] & LQSPI_CFG_TWO_MEM) ? s->num_busses : 1; + return (s->regs[R_LQSPI_CFG] & LQSPI_CFG_SEP_BUS && + s->regs[R_LQSPI_CFG] & LQSPI_CFG_TWO_MEM) ? s->num_busses : 1; } static void xilinx_spips_update_cs_lines(XilinxSPIPS *s) @@ -197,7 +215,7 @@ static void xilinx_spips_update_ixr(XilinxSPIPS *s) static void xilinx_spips_reset(DeviceState *d) { - XilinxSPIPS *s = DO_UPCAST(XilinxSPIPS, busdev.qdev, d); + XilinxSPIPS *s = XILINX_SPIPS(d); int i; for (i = 0; i < R_MAX; i++) { @@ -251,15 +269,19 @@ static void xilinx_spips_flush_txfifo(XilinxSPIPS *s) switch (s->snoop_state) { case (SNOOP_CHECKING): switch (tx) { /* new instruction code */ - case 0x0b: /* dual/quad output read DOR/QOR */ - case 0x6b: - s->snoop_state = 4; + case READ: /* 3 address bytes, no dummy bytes/cycles */ + case PP: + case DPP: + case QPP: + s->snoop_state = 3; break; - /* FIXME: these vary between vendor - set to spansion */ - case 0xbb: /* high performance dual read DIOR */ + case FAST_READ: /* 3 address bytes, 1 dummy byte */ + case DOR: + case QOR: + case DIOR: /* FIXME: these vary between vendor - set to spansion */ s->snoop_state = 4; break; - case 0xeb: /* high performance quad read QIOR */ + case QIOR: /* 3 address bytes, 2 dummy bytes */ s->snoop_state = 6; break; default: @@ -483,9 +505,10 @@ static const MemoryRegionOps lqspi_ops = { } }; -static int xilinx_spips_init(SysBusDevice *dev) +static void xilinx_spips_realize(DeviceState *dev, Error **errp) { - XilinxSPIPS *s = FROM_SYSBUS(typeof(*s), dev); + XilinxSPIPS *s = XILINX_SPIPS(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); int i; DB_PRINT("inited device model\n"); @@ -494,31 +517,29 @@ static int xilinx_spips_init(SysBusDevice *dev) for (i = 0; i < s->num_busses; ++i) { char bus_name[16]; snprintf(bus_name, 16, "spi%d", i); - s->spi[i] = ssi_create_bus(&dev->qdev, bus_name); + s->spi[i] = ssi_create_bus(dev, bus_name); } - s->cs_lines = g_new(qemu_irq, s->num_cs * s->num_busses); + s->cs_lines = g_new0(qemu_irq, s->num_cs * s->num_busses); ssi_auto_connect_slaves(DEVICE(s), s->cs_lines, s->spi[0]); ssi_auto_connect_slaves(DEVICE(s), s->cs_lines, s->spi[1]); - sysbus_init_irq(dev, &s->irq); + sysbus_init_irq(sbd, &s->irq); for (i = 0; i < s->num_cs * s->num_busses; ++i) { - sysbus_init_irq(dev, &s->cs_lines[i]); + sysbus_init_irq(sbd, &s->cs_lines[i]); } memory_region_init_io(&s->iomem, &spips_ops, s, "spi", R_MAX*4); - sysbus_init_mmio(dev, &s->iomem); + sysbus_init_mmio(sbd, &s->iomem); memory_region_init_io(&s->mmlqspi, &lqspi_ops, s, "lqspi", (1 << LQSPI_ADDRESS_BITS) * 2); - sysbus_init_mmio(dev, &s->mmlqspi); + sysbus_init_mmio(sbd, &s->mmlqspi); s->irqline = -1; s->lqspi_cached_addr = ~0ULL; fifo8_create(&s->rx_fifo, RXFF_A); fifo8_create(&s->tx_fifo, TXFF_A); - - return 0; } static int xilinx_spips_post_load(void *opaque, int version_id) @@ -552,16 +573,15 @@ static Property xilinx_spips_properties[] = { static void xilinx_spips_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - sdc->init = xilinx_spips_init; + dc->realize = xilinx_spips_realize; dc->reset = xilinx_spips_reset; dc->props = xilinx_spips_properties; dc->vmsd = &vmstate_xilinx_spips; } static const TypeInfo xilinx_spips_info = { - .name = "xilinx,spips", + .name = TYPE_XILINX_SPIPS, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(XilinxSPIPS), .class_init = xilinx_spips_class_init, |