bochs-display.c 11 KB

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