1*765c9429SGerd Hoffmann /* 2*765c9429SGerd Hoffmann * QEMU PCI bochs display adapter. 3*765c9429SGerd Hoffmann * 4*765c9429SGerd Hoffmann * This work is licensed under the terms of the GNU GPL, version 2 or later. 5*765c9429SGerd Hoffmann * See the COPYING file in the top-level directory. 6*765c9429SGerd Hoffmann */ 7*765c9429SGerd Hoffmann #include "qemu/osdep.h" 8*765c9429SGerd Hoffmann #include "hw/hw.h" 9*765c9429SGerd Hoffmann #include "hw/pci/pci.h" 10*765c9429SGerd Hoffmann #include "hw/display/bochs-vbe.h" 11*765c9429SGerd Hoffmann 12*765c9429SGerd Hoffmann #include "qapi/error.h" 13*765c9429SGerd Hoffmann 14*765c9429SGerd Hoffmann #include "ui/console.h" 15*765c9429SGerd Hoffmann #include "ui/qemu-pixman.h" 16*765c9429SGerd Hoffmann 17*765c9429SGerd Hoffmann typedef struct BochsDisplayMode { 18*765c9429SGerd Hoffmann pixman_format_code_t format; 19*765c9429SGerd Hoffmann uint32_t bytepp; 20*765c9429SGerd Hoffmann uint32_t width; 21*765c9429SGerd Hoffmann uint32_t height; 22*765c9429SGerd Hoffmann uint32_t stride; 23*765c9429SGerd Hoffmann uint64_t offset; 24*765c9429SGerd Hoffmann uint64_t size; 25*765c9429SGerd Hoffmann } BochsDisplayMode; 26*765c9429SGerd Hoffmann 27*765c9429SGerd Hoffmann typedef struct BochsDisplayState { 28*765c9429SGerd Hoffmann /* parent */ 29*765c9429SGerd Hoffmann PCIDevice pci; 30*765c9429SGerd Hoffmann 31*765c9429SGerd Hoffmann /* device elements */ 32*765c9429SGerd Hoffmann QemuConsole *con; 33*765c9429SGerd Hoffmann MemoryRegion vram; 34*765c9429SGerd Hoffmann MemoryRegion mmio; 35*765c9429SGerd Hoffmann MemoryRegion vbe; 36*765c9429SGerd Hoffmann MemoryRegion qext; 37*765c9429SGerd Hoffmann 38*765c9429SGerd Hoffmann /* device config */ 39*765c9429SGerd Hoffmann uint64_t vgamem; 40*765c9429SGerd Hoffmann 41*765c9429SGerd Hoffmann /* device registers */ 42*765c9429SGerd Hoffmann uint16_t vbe_regs[VBE_DISPI_INDEX_NB]; 43*765c9429SGerd Hoffmann bool big_endian_fb; 44*765c9429SGerd Hoffmann 45*765c9429SGerd Hoffmann /* device state */ 46*765c9429SGerd Hoffmann BochsDisplayMode mode; 47*765c9429SGerd Hoffmann } BochsDisplayState; 48*765c9429SGerd Hoffmann 49*765c9429SGerd Hoffmann #define TYPE_BOCHS_DISPLAY "bochs-display" 50*765c9429SGerd Hoffmann #define BOCHS_DISPLAY(obj) OBJECT_CHECK(BochsDisplayState, (obj), \ 51*765c9429SGerd Hoffmann TYPE_BOCHS_DISPLAY) 52*765c9429SGerd Hoffmann 53*765c9429SGerd Hoffmann static const VMStateDescription vmstate_bochs_display = { 54*765c9429SGerd Hoffmann .name = "bochs-display", 55*765c9429SGerd Hoffmann .fields = (VMStateField[]) { 56*765c9429SGerd Hoffmann VMSTATE_PCI_DEVICE(pci, BochsDisplayState), 57*765c9429SGerd Hoffmann VMSTATE_UINT16_ARRAY(vbe_regs, BochsDisplayState, VBE_DISPI_INDEX_NB), 58*765c9429SGerd Hoffmann VMSTATE_BOOL(big_endian_fb, BochsDisplayState), 59*765c9429SGerd Hoffmann VMSTATE_END_OF_LIST() 60*765c9429SGerd Hoffmann } 61*765c9429SGerd Hoffmann }; 62*765c9429SGerd Hoffmann 63*765c9429SGerd Hoffmann static uint64_t bochs_display_vbe_read(void *ptr, hwaddr addr, 64*765c9429SGerd Hoffmann unsigned size) 65*765c9429SGerd Hoffmann { 66*765c9429SGerd Hoffmann BochsDisplayState *s = ptr; 67*765c9429SGerd Hoffmann unsigned int index = addr >> 1; 68*765c9429SGerd Hoffmann 69*765c9429SGerd Hoffmann switch (index) { 70*765c9429SGerd Hoffmann case VBE_DISPI_INDEX_ID: 71*765c9429SGerd Hoffmann return VBE_DISPI_ID5; 72*765c9429SGerd Hoffmann case VBE_DISPI_INDEX_VIDEO_MEMORY_64K: 73*765c9429SGerd Hoffmann return s->vgamem / (64 * 1024); 74*765c9429SGerd Hoffmann } 75*765c9429SGerd Hoffmann 76*765c9429SGerd Hoffmann if (index >= ARRAY_SIZE(s->vbe_regs)) { 77*765c9429SGerd Hoffmann return -1; 78*765c9429SGerd Hoffmann } 79*765c9429SGerd Hoffmann return s->vbe_regs[index]; 80*765c9429SGerd Hoffmann } 81*765c9429SGerd Hoffmann 82*765c9429SGerd Hoffmann static void bochs_display_vbe_write(void *ptr, hwaddr addr, 83*765c9429SGerd Hoffmann uint64_t val, unsigned size) 84*765c9429SGerd Hoffmann { 85*765c9429SGerd Hoffmann BochsDisplayState *s = ptr; 86*765c9429SGerd Hoffmann unsigned int index = addr >> 1; 87*765c9429SGerd Hoffmann 88*765c9429SGerd Hoffmann if (index >= ARRAY_SIZE(s->vbe_regs)) { 89*765c9429SGerd Hoffmann return; 90*765c9429SGerd Hoffmann } 91*765c9429SGerd Hoffmann s->vbe_regs[index] = val; 92*765c9429SGerd Hoffmann } 93*765c9429SGerd Hoffmann 94*765c9429SGerd Hoffmann static const MemoryRegionOps bochs_display_vbe_ops = { 95*765c9429SGerd Hoffmann .read = bochs_display_vbe_read, 96*765c9429SGerd Hoffmann .write = bochs_display_vbe_write, 97*765c9429SGerd Hoffmann .valid.min_access_size = 1, 98*765c9429SGerd Hoffmann .valid.max_access_size = 4, 99*765c9429SGerd Hoffmann .impl.min_access_size = 2, 100*765c9429SGerd Hoffmann .impl.max_access_size = 2, 101*765c9429SGerd Hoffmann .endianness = DEVICE_LITTLE_ENDIAN, 102*765c9429SGerd Hoffmann }; 103*765c9429SGerd Hoffmann 104*765c9429SGerd Hoffmann static uint64_t bochs_display_qext_read(void *ptr, hwaddr addr, 105*765c9429SGerd Hoffmann unsigned size) 106*765c9429SGerd Hoffmann { 107*765c9429SGerd Hoffmann BochsDisplayState *s = ptr; 108*765c9429SGerd Hoffmann 109*765c9429SGerd Hoffmann switch (addr) { 110*765c9429SGerd Hoffmann case PCI_VGA_QEXT_REG_SIZE: 111*765c9429SGerd Hoffmann return PCI_VGA_QEXT_SIZE; 112*765c9429SGerd Hoffmann case PCI_VGA_QEXT_REG_BYTEORDER: 113*765c9429SGerd Hoffmann return s->big_endian_fb ? 114*765c9429SGerd Hoffmann PCI_VGA_QEXT_BIG_ENDIAN : PCI_VGA_QEXT_LITTLE_ENDIAN; 115*765c9429SGerd Hoffmann default: 116*765c9429SGerd Hoffmann return 0; 117*765c9429SGerd Hoffmann } 118*765c9429SGerd Hoffmann } 119*765c9429SGerd Hoffmann 120*765c9429SGerd Hoffmann static void bochs_display_qext_write(void *ptr, hwaddr addr, 121*765c9429SGerd Hoffmann uint64_t val, unsigned size) 122*765c9429SGerd Hoffmann { 123*765c9429SGerd Hoffmann BochsDisplayState *s = ptr; 124*765c9429SGerd Hoffmann 125*765c9429SGerd Hoffmann switch (addr) { 126*765c9429SGerd Hoffmann case PCI_VGA_QEXT_REG_BYTEORDER: 127*765c9429SGerd Hoffmann if (val == PCI_VGA_QEXT_BIG_ENDIAN) { 128*765c9429SGerd Hoffmann s->big_endian_fb = true; 129*765c9429SGerd Hoffmann } 130*765c9429SGerd Hoffmann if (val == PCI_VGA_QEXT_LITTLE_ENDIAN) { 131*765c9429SGerd Hoffmann s->big_endian_fb = false; 132*765c9429SGerd Hoffmann } 133*765c9429SGerd Hoffmann break; 134*765c9429SGerd Hoffmann } 135*765c9429SGerd Hoffmann } 136*765c9429SGerd Hoffmann 137*765c9429SGerd Hoffmann static const MemoryRegionOps bochs_display_qext_ops = { 138*765c9429SGerd Hoffmann .read = bochs_display_qext_read, 139*765c9429SGerd Hoffmann .write = bochs_display_qext_write, 140*765c9429SGerd Hoffmann .valid.min_access_size = 4, 141*765c9429SGerd Hoffmann .valid.max_access_size = 4, 142*765c9429SGerd Hoffmann .endianness = DEVICE_LITTLE_ENDIAN, 143*765c9429SGerd Hoffmann }; 144*765c9429SGerd Hoffmann 145*765c9429SGerd Hoffmann static int bochs_display_get_mode(BochsDisplayState *s, 146*765c9429SGerd Hoffmann BochsDisplayMode *mode) 147*765c9429SGerd Hoffmann { 148*765c9429SGerd Hoffmann uint16_t *vbe = s->vbe_regs; 149*765c9429SGerd Hoffmann uint32_t virt_width; 150*765c9429SGerd Hoffmann 151*765c9429SGerd Hoffmann if (!(vbe[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) { 152*765c9429SGerd Hoffmann return -1; 153*765c9429SGerd Hoffmann } 154*765c9429SGerd Hoffmann 155*765c9429SGerd Hoffmann memset(mode, 0, sizeof(*mode)); 156*765c9429SGerd Hoffmann switch (vbe[VBE_DISPI_INDEX_BPP]) { 157*765c9429SGerd Hoffmann case 16: 158*765c9429SGerd Hoffmann /* best effort: support native endianess only */ 159*765c9429SGerd Hoffmann mode->format = PIXMAN_r5g6b5; 160*765c9429SGerd Hoffmann mode->bytepp = 2; 161*765c9429SGerd Hoffmann case 32: 162*765c9429SGerd Hoffmann mode->format = s->big_endian_fb 163*765c9429SGerd Hoffmann ? PIXMAN_BE_x8r8g8b8 164*765c9429SGerd Hoffmann : PIXMAN_LE_x8r8g8b8; 165*765c9429SGerd Hoffmann mode->bytepp = 4; 166*765c9429SGerd Hoffmann break; 167*765c9429SGerd Hoffmann default: 168*765c9429SGerd Hoffmann return -1; 169*765c9429SGerd Hoffmann } 170*765c9429SGerd Hoffmann 171*765c9429SGerd Hoffmann mode->width = vbe[VBE_DISPI_INDEX_XRES]; 172*765c9429SGerd Hoffmann mode->height = vbe[VBE_DISPI_INDEX_YRES]; 173*765c9429SGerd Hoffmann virt_width = vbe[VBE_DISPI_INDEX_VIRT_WIDTH]; 174*765c9429SGerd Hoffmann if (virt_width < mode->width) { 175*765c9429SGerd Hoffmann virt_width = mode->width; 176*765c9429SGerd Hoffmann } 177*765c9429SGerd Hoffmann mode->stride = virt_width * mode->bytepp; 178*765c9429SGerd Hoffmann mode->size = (uint64_t)mode->stride * mode->height; 179*765c9429SGerd Hoffmann mode->offset = ((uint64_t)vbe[VBE_DISPI_INDEX_X_OFFSET] * mode->bytepp + 180*765c9429SGerd Hoffmann (uint64_t)vbe[VBE_DISPI_INDEX_Y_OFFSET] * mode->stride); 181*765c9429SGerd Hoffmann 182*765c9429SGerd Hoffmann if (mode->width < 64 || mode->height < 64) { 183*765c9429SGerd Hoffmann return -1; 184*765c9429SGerd Hoffmann } 185*765c9429SGerd Hoffmann if (mode->offset + mode->size > s->vgamem) { 186*765c9429SGerd Hoffmann return -1; 187*765c9429SGerd Hoffmann } 188*765c9429SGerd Hoffmann return 0; 189*765c9429SGerd Hoffmann } 190*765c9429SGerd Hoffmann 191*765c9429SGerd Hoffmann static void bochs_display_update(void *opaque) 192*765c9429SGerd Hoffmann { 193*765c9429SGerd Hoffmann BochsDisplayState *s = opaque; 194*765c9429SGerd Hoffmann BochsDisplayMode mode; 195*765c9429SGerd Hoffmann DisplaySurface *ds; 196*765c9429SGerd Hoffmann uint8_t *ptr; 197*765c9429SGerd Hoffmann int ret; 198*765c9429SGerd Hoffmann 199*765c9429SGerd Hoffmann ret = bochs_display_get_mode(s, &mode); 200*765c9429SGerd Hoffmann if (ret < 0) { 201*765c9429SGerd Hoffmann /* no (valid) video mode */ 202*765c9429SGerd Hoffmann return; 203*765c9429SGerd Hoffmann } 204*765c9429SGerd Hoffmann 205*765c9429SGerd Hoffmann if (memcmp(&s->mode, &mode, sizeof(mode)) != 0) { 206*765c9429SGerd Hoffmann /* video mode switch */ 207*765c9429SGerd Hoffmann s->mode = mode; 208*765c9429SGerd Hoffmann ptr = memory_region_get_ram_ptr(&s->vram); 209*765c9429SGerd Hoffmann ds = qemu_create_displaysurface_from(mode.width, 210*765c9429SGerd Hoffmann mode.height, 211*765c9429SGerd Hoffmann mode.format, 212*765c9429SGerd Hoffmann mode.stride, 213*765c9429SGerd Hoffmann ptr + mode.offset); 214*765c9429SGerd Hoffmann dpy_gfx_replace_surface(s->con, ds); 215*765c9429SGerd Hoffmann } 216*765c9429SGerd Hoffmann 217*765c9429SGerd Hoffmann dpy_gfx_update_full(s->con); 218*765c9429SGerd Hoffmann } 219*765c9429SGerd Hoffmann 220*765c9429SGerd Hoffmann static const GraphicHwOps bochs_display_gfx_ops = { 221*765c9429SGerd Hoffmann .gfx_update = bochs_display_update, 222*765c9429SGerd Hoffmann }; 223*765c9429SGerd Hoffmann 224*765c9429SGerd Hoffmann static void bochs_display_realize(PCIDevice *dev, Error **errp) 225*765c9429SGerd Hoffmann { 226*765c9429SGerd Hoffmann BochsDisplayState *s = BOCHS_DISPLAY(dev); 227*765c9429SGerd Hoffmann Object *obj = OBJECT(dev); 228*765c9429SGerd Hoffmann 229*765c9429SGerd Hoffmann s->con = graphic_console_init(DEVICE(dev), 0, &bochs_display_gfx_ops, s); 230*765c9429SGerd Hoffmann 231*765c9429SGerd Hoffmann if (s->vgamem < (4 * 1024 * 1024)) { 232*765c9429SGerd Hoffmann error_setg(errp, "bochs-display: video memory too small"); 233*765c9429SGerd Hoffmann } 234*765c9429SGerd Hoffmann if (s->vgamem > (256 * 1024 * 1024)) { 235*765c9429SGerd Hoffmann error_setg(errp, "bochs-display: video memory too big"); 236*765c9429SGerd Hoffmann } 237*765c9429SGerd Hoffmann s->vgamem = pow2ceil(s->vgamem); 238*765c9429SGerd Hoffmann 239*765c9429SGerd Hoffmann memory_region_init_ram(&s->vram, obj, "bochs-display-vram", s->vgamem, 240*765c9429SGerd Hoffmann &error_fatal); 241*765c9429SGerd Hoffmann memory_region_init_io(&s->vbe, obj, &bochs_display_vbe_ops, s, 242*765c9429SGerd Hoffmann "bochs dispi interface", PCI_VGA_BOCHS_SIZE); 243*765c9429SGerd Hoffmann memory_region_init_io(&s->qext, obj, &bochs_display_qext_ops, s, 244*765c9429SGerd Hoffmann "qemu extended regs", PCI_VGA_QEXT_SIZE); 245*765c9429SGerd Hoffmann 246*765c9429SGerd Hoffmann memory_region_init(&s->mmio, obj, "bochs-display-mmio", 247*765c9429SGerd Hoffmann PCI_VGA_MMIO_SIZE); 248*765c9429SGerd Hoffmann memory_region_add_subregion(&s->mmio, PCI_VGA_BOCHS_OFFSET, &s->vbe); 249*765c9429SGerd Hoffmann memory_region_add_subregion(&s->mmio, PCI_VGA_QEXT_OFFSET, &s->qext); 250*765c9429SGerd Hoffmann 251*765c9429SGerd Hoffmann pci_set_byte(&s->pci.config[PCI_REVISION_ID], 2); 252*765c9429SGerd Hoffmann pci_register_bar(&s->pci, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram); 253*765c9429SGerd Hoffmann pci_register_bar(&s->pci, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio); 254*765c9429SGerd Hoffmann } 255*765c9429SGerd Hoffmann 256*765c9429SGerd Hoffmann static bool bochs_display_get_big_endian_fb(Object *obj, Error **errp) 257*765c9429SGerd Hoffmann { 258*765c9429SGerd Hoffmann BochsDisplayState *s = BOCHS_DISPLAY(obj); 259*765c9429SGerd Hoffmann 260*765c9429SGerd Hoffmann return s->big_endian_fb; 261*765c9429SGerd Hoffmann } 262*765c9429SGerd Hoffmann 263*765c9429SGerd Hoffmann static void bochs_display_set_big_endian_fb(Object *obj, bool value, 264*765c9429SGerd Hoffmann Error **errp) 265*765c9429SGerd Hoffmann { 266*765c9429SGerd Hoffmann BochsDisplayState *s = BOCHS_DISPLAY(obj); 267*765c9429SGerd Hoffmann 268*765c9429SGerd Hoffmann s->big_endian_fb = value; 269*765c9429SGerd Hoffmann } 270*765c9429SGerd Hoffmann 271*765c9429SGerd Hoffmann static void bochs_display_init(Object *obj) 272*765c9429SGerd Hoffmann { 273*765c9429SGerd Hoffmann /* Expose framebuffer byteorder via QOM */ 274*765c9429SGerd Hoffmann object_property_add_bool(obj, "big-endian-framebuffer", 275*765c9429SGerd Hoffmann bochs_display_get_big_endian_fb, 276*765c9429SGerd Hoffmann bochs_display_set_big_endian_fb, 277*765c9429SGerd Hoffmann NULL); 278*765c9429SGerd Hoffmann } 279*765c9429SGerd Hoffmann 280*765c9429SGerd Hoffmann static void bochs_display_exit(PCIDevice *dev) 281*765c9429SGerd Hoffmann { 282*765c9429SGerd Hoffmann BochsDisplayState *s = BOCHS_DISPLAY(dev); 283*765c9429SGerd Hoffmann 284*765c9429SGerd Hoffmann graphic_console_close(s->con); 285*765c9429SGerd Hoffmann } 286*765c9429SGerd Hoffmann 287*765c9429SGerd Hoffmann static Property bochs_display_properties[] = { 288*765c9429SGerd Hoffmann DEFINE_PROP_SIZE("vgamem", BochsDisplayState, vgamem, 16 * 1024 * 1024), 289*765c9429SGerd Hoffmann DEFINE_PROP_END_OF_LIST(), 290*765c9429SGerd Hoffmann }; 291*765c9429SGerd Hoffmann 292*765c9429SGerd Hoffmann static void bochs_display_class_init(ObjectClass *klass, void *data) 293*765c9429SGerd Hoffmann { 294*765c9429SGerd Hoffmann DeviceClass *dc = DEVICE_CLASS(klass); 295*765c9429SGerd Hoffmann PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); 296*765c9429SGerd Hoffmann 297*765c9429SGerd Hoffmann k->class_id = PCI_CLASS_DISPLAY_OTHER; 298*765c9429SGerd Hoffmann k->vendor_id = PCI_VENDOR_ID_QEMU; 299*765c9429SGerd Hoffmann k->device_id = PCI_DEVICE_ID_QEMU_VGA; 300*765c9429SGerd Hoffmann 301*765c9429SGerd Hoffmann k->realize = bochs_display_realize; 302*765c9429SGerd Hoffmann k->exit = bochs_display_exit; 303*765c9429SGerd Hoffmann dc->vmsd = &vmstate_bochs_display; 304*765c9429SGerd Hoffmann dc->props = bochs_display_properties; 305*765c9429SGerd Hoffmann set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); 306*765c9429SGerd Hoffmann } 307*765c9429SGerd Hoffmann 308*765c9429SGerd Hoffmann static const TypeInfo bochs_display_type_info = { 309*765c9429SGerd Hoffmann .name = TYPE_BOCHS_DISPLAY, 310*765c9429SGerd Hoffmann .parent = TYPE_PCI_DEVICE, 311*765c9429SGerd Hoffmann .instance_size = sizeof(BochsDisplayState), 312*765c9429SGerd Hoffmann .instance_init = bochs_display_init, 313*765c9429SGerd Hoffmann .class_init = bochs_display_class_init, 314*765c9429SGerd Hoffmann .interfaces = (InterfaceInfo[]) { 315*765c9429SGerd Hoffmann { INTERFACE_CONVENTIONAL_PCI_DEVICE }, 316*765c9429SGerd Hoffmann { }, 317*765c9429SGerd Hoffmann }, 318*765c9429SGerd Hoffmann }; 319*765c9429SGerd Hoffmann 320*765c9429SGerd Hoffmann static void bochs_display_register_types(void) 321*765c9429SGerd Hoffmann { 322*765c9429SGerd Hoffmann type_register_static(&bochs_display_type_info); 323*765c9429SGerd Hoffmann } 324*765c9429SGerd Hoffmann 325*765c9429SGerd Hoffmann type_init(bochs_display_register_types) 326