GraphicsAdapter.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. /*
  2. * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Atomic.h>
  7. #include <AK/Checked.h>
  8. #include <Kernel/Arch/x86/IO.h>
  9. #include <Kernel/Bus/PCI/API.h>
  10. #include <Kernel/Bus/PCI/IDs.h>
  11. #include <Kernel/Debug.h>
  12. #include <Kernel/Graphics/Bochs/GraphicsAdapter.h>
  13. #include <Kernel/Graphics/Console/ContiguousFramebufferConsole.h>
  14. #include <Kernel/Graphics/GraphicsManagement.h>
  15. #include <Kernel/Memory/TypedMapping.h>
  16. #include <Kernel/Sections.h>
  17. #define VBE_DISPI_IOPORT_INDEX 0x01CE
  18. #define VBE_DISPI_IOPORT_DATA 0x01CF
  19. #define VBE_DISPI_ID5 0xB0C5
  20. #define BOCHS_DISPLAY_LITTLE_ENDIAN 0x1e1e1e1e
  21. #define BOCHS_DISPLAY_BIG_ENDIAN 0xbebebebe
  22. namespace Kernel {
  23. enum class BochsFramebufferSettings {
  24. Enabled = 0x1,
  25. LinearFramebuffer = 0x40,
  26. };
  27. enum class BochsDISPIRegisters {
  28. ID = 0x0,
  29. XRES = 0x1,
  30. YRES = 0x2,
  31. BPP = 0x3,
  32. ENABLE = 0x4,
  33. BANK = 0x5,
  34. VIRT_WIDTH = 0x6,
  35. VIRT_HEIGHT = 0x7,
  36. X_OFFSET = 0x8,
  37. Y_OFFSET = 0x9,
  38. };
  39. struct [[gnu::packed]] DISPIInterface {
  40. u16 index_id;
  41. u16 xres;
  42. u16 yres;
  43. u16 bpp;
  44. u16 enable;
  45. u16 bank;
  46. u16 virt_width;
  47. u16 virt_height;
  48. u16 x_offset;
  49. u16 y_offset;
  50. };
  51. struct [[gnu::packed]] ExtensionRegisters {
  52. u32 region_size;
  53. u32 framebuffer_byteorder;
  54. };
  55. struct [[gnu::packed]] BochsDisplayMMIORegisters {
  56. u8 edid_data[0x400];
  57. u16 vga_ioports[0x10];
  58. u8 reserved[0xE0];
  59. DISPIInterface bochs_regs;
  60. u8 reserved2[0x100 - sizeof(DISPIInterface)];
  61. ExtensionRegisters extension_regs;
  62. };
  63. UNMAP_AFTER_INIT NonnullRefPtr<BochsGraphicsAdapter> BochsGraphicsAdapter::initialize(PCI::DeviceIdentifier const& pci_device_identifier)
  64. {
  65. PCI::HardwareID id = pci_device_identifier.hardware_id();
  66. VERIFY((id.vendor_id == PCI::VendorID::QEMUOld && id.device_id == 0x1111) || (id.vendor_id == PCI::VendorID::VirtualBox && id.device_id == 0xbeef));
  67. return adopt_ref(*new BochsGraphicsAdapter(pci_device_identifier));
  68. }
  69. void BochsGraphicsAdapter::set_framebuffer_to_big_endian_format()
  70. {
  71. dbgln_if(BXVGA_DEBUG, "BochsGraphicsAdapter set_framebuffer_to_big_endian_format");
  72. full_memory_barrier();
  73. if (m_registers->extension_regs.region_size == 0xFFFFFFFF || m_registers->extension_regs.region_size == 0)
  74. return;
  75. full_memory_barrier();
  76. m_registers->extension_regs.framebuffer_byteorder = BOCHS_DISPLAY_BIG_ENDIAN;
  77. full_memory_barrier();
  78. }
  79. void BochsGraphicsAdapter::set_framebuffer_to_little_endian_format()
  80. {
  81. dbgln_if(BXVGA_DEBUG, "BochsGraphicsAdapter set_framebuffer_to_little_endian_format");
  82. full_memory_barrier();
  83. if (m_registers->extension_regs.region_size == 0xFFFFFFFF || m_registers->extension_regs.region_size == 0)
  84. return;
  85. full_memory_barrier();
  86. m_registers->extension_regs.framebuffer_byteorder = BOCHS_DISPLAY_LITTLE_ENDIAN;
  87. full_memory_barrier();
  88. }
  89. UNMAP_AFTER_INIT BochsGraphicsAdapter::BochsGraphicsAdapter(PCI::DeviceIdentifier const& pci_device_identifier)
  90. : PCI::Device(pci_device_identifier.address())
  91. , m_mmio_registers(PCI::get_BAR2(pci_device_identifier.address()) & 0xfffffff0)
  92. , m_registers(Memory::map_typed_writable<BochsDisplayMMIORegisters volatile>(m_mmio_registers).release_value_but_fixme_should_propagate_errors())
  93. {
  94. // We assume safe resolution is 1024x768x32
  95. m_framebuffer_console = Graphics::ContiguousFramebufferConsole::initialize(PhysicalAddress(PCI::get_BAR0(pci_device_identifier.address()) & 0xfffffff0), 1024, 768, 1024 * sizeof(u32));
  96. GraphicsManagement::the().set_console(*m_framebuffer_console);
  97. // Note: If we use VirtualBox graphics adapter (which is based on Bochs one), we need to use IO ports
  98. if (pci_device_identifier.hardware_id().vendor_id == 0x80ee && pci_device_identifier.hardware_id().device_id == 0xbeef)
  99. m_io_required = true;
  100. if (pci_device_identifier.class_code().value() == 0x3 && pci_device_identifier.subclass_code().value() == 0x0)
  101. m_is_vga_capable = true;
  102. // Note: According to Gerd Hoffmann - "The linux driver simply does
  103. // the unblank unconditionally. With bochs-display this is not needed but
  104. // it also has no bad side effect".
  105. unblank();
  106. set_safe_resolution();
  107. }
  108. UNMAP_AFTER_INIT void BochsGraphicsAdapter::initialize_framebuffer_devices()
  109. {
  110. // FIXME: Find a better way to determine default resolution...
  111. m_framebuffer_device = FramebufferDevice::create(*this, PhysicalAddress(PCI::get_BAR0(pci_address()) & 0xfffffff0), 1024, 768, 1024 * sizeof(u32));
  112. // While write-combine helps greatly on actual hardware, it greatly reduces performance in QEMU
  113. m_framebuffer_device->enable_write_combine(false);
  114. // FIXME: Would be nice to be able to return a ErrorOr<void> here.
  115. VERIFY(!m_framebuffer_device->try_to_initialize().is_error());
  116. }
  117. bool BochsGraphicsAdapter::vga_compatible() const
  118. {
  119. return m_is_vga_capable;
  120. }
  121. void BochsGraphicsAdapter::unblank()
  122. {
  123. full_memory_barrier();
  124. m_registers->vga_ioports[0] = 0x20;
  125. full_memory_barrier();
  126. }
  127. void BochsGraphicsAdapter::set_safe_resolution()
  128. {
  129. VERIFY(m_framebuffer_console);
  130. auto result = try_to_set_resolution(0, 1024, 768);
  131. VERIFY(result);
  132. }
  133. static void set_register_with_io(u16 index, u16 data)
  134. {
  135. IO::out16(VBE_DISPI_IOPORT_INDEX, index);
  136. IO::out16(VBE_DISPI_IOPORT_DATA, data);
  137. }
  138. static u16 get_register_with_io(u16 index)
  139. {
  140. IO::out16(VBE_DISPI_IOPORT_INDEX, index);
  141. return IO::in16(VBE_DISPI_IOPORT_DATA);
  142. }
  143. BochsGraphicsAdapter::IndexID BochsGraphicsAdapter::index_id() const
  144. {
  145. if (m_io_required) {
  146. return get_register_with_io(0);
  147. }
  148. return m_registers->bochs_regs.index_id;
  149. }
  150. void BochsGraphicsAdapter::set_resolution_registers_via_io(size_t width, size_t height)
  151. {
  152. dbgln_if(BXVGA_DEBUG, "BochsGraphicsAdapter resolution registers set to - {}x{}", width, height);
  153. set_register_with_io(to_underlying(BochsDISPIRegisters::ENABLE), 0);
  154. set_register_with_io(to_underlying(BochsDISPIRegisters::XRES), (u16)width);
  155. set_register_with_io(to_underlying(BochsDISPIRegisters::YRES), (u16)height);
  156. set_register_with_io(to_underlying(BochsDISPIRegisters::VIRT_WIDTH), (u16)width);
  157. set_register_with_io(to_underlying(BochsDISPIRegisters::VIRT_HEIGHT), (u16)height * 2);
  158. set_register_with_io(to_underlying(BochsDISPIRegisters::BPP), 32);
  159. set_register_with_io(to_underlying(BochsDISPIRegisters::ENABLE), to_underlying(BochsFramebufferSettings::Enabled) | to_underlying(BochsFramebufferSettings::LinearFramebuffer));
  160. set_register_with_io(to_underlying(BochsDISPIRegisters::BANK), 0);
  161. }
  162. void BochsGraphicsAdapter::set_resolution_registers(size_t width, size_t height)
  163. {
  164. dbgln_if(BXVGA_DEBUG, "BochsGraphicsAdapter resolution registers set to - {}x{}", width, height);
  165. m_registers->bochs_regs.enable = 0;
  166. full_memory_barrier();
  167. m_registers->bochs_regs.xres = width;
  168. m_registers->bochs_regs.yres = height;
  169. m_registers->bochs_regs.virt_width = width;
  170. m_registers->bochs_regs.virt_height = height * 2;
  171. m_registers->bochs_regs.bpp = 32;
  172. full_memory_barrier();
  173. m_registers->bochs_regs.enable = to_underlying(BochsFramebufferSettings::Enabled) | to_underlying(BochsFramebufferSettings::LinearFramebuffer);
  174. full_memory_barrier();
  175. m_registers->bochs_regs.bank = 0;
  176. if (index_id().value() == VBE_DISPI_ID5) {
  177. set_framebuffer_to_little_endian_format();
  178. }
  179. }
  180. bool BochsGraphicsAdapter::try_to_set_resolution(size_t output_port_index, size_t width, size_t height)
  181. {
  182. // Note: There's only one output port for this adapter
  183. VERIFY(output_port_index == 0);
  184. VERIFY(m_framebuffer_console);
  185. if (Checked<size_t>::multiplication_would_overflow(width, height, sizeof(u32)))
  186. return false;
  187. if (m_io_required)
  188. set_resolution_registers_via_io(width, height);
  189. else
  190. set_resolution_registers(width, height);
  191. dbgln_if(BXVGA_DEBUG, "BochsGraphicsAdapter resolution test - {}x{}", width, height);
  192. if (m_io_required) {
  193. if (!validate_setup_resolution_with_io(width, height))
  194. return false;
  195. } else {
  196. if (!validate_setup_resolution(width, height))
  197. return false;
  198. }
  199. dbgln("BochsGraphicsAdapter: resolution set to {}x{}", width, height);
  200. m_framebuffer_console->set_resolution(width, height, width * sizeof(u32));
  201. return true;
  202. }
  203. bool BochsGraphicsAdapter::validate_setup_resolution_with_io(size_t width, size_t height)
  204. {
  205. if ((u16)width != get_register_with_io(to_underlying(BochsDISPIRegisters::XRES)) || (u16)height != get_register_with_io(to_underlying(BochsDISPIRegisters::YRES))) {
  206. return false;
  207. }
  208. return true;
  209. }
  210. bool BochsGraphicsAdapter::validate_setup_resolution(size_t width, size_t height)
  211. {
  212. if ((u16)width != m_registers->bochs_regs.xres || (u16)height != m_registers->bochs_regs.yres) {
  213. return false;
  214. }
  215. return true;
  216. }
  217. bool BochsGraphicsAdapter::set_y_offset(size_t output_port_index, size_t y_offset)
  218. {
  219. VERIFY(output_port_index == 0);
  220. if (m_console_enabled)
  221. return false;
  222. m_registers->bochs_regs.y_offset = y_offset;
  223. return true;
  224. }
  225. void BochsGraphicsAdapter::enable_consoles()
  226. {
  227. SpinlockLocker lock(m_console_mode_switch_lock);
  228. VERIFY(m_framebuffer_console);
  229. m_console_enabled = true;
  230. m_registers->bochs_regs.y_offset = 0;
  231. if (m_framebuffer_device)
  232. m_framebuffer_device->deactivate_writes();
  233. m_framebuffer_console->enable();
  234. }
  235. void BochsGraphicsAdapter::disable_consoles()
  236. {
  237. SpinlockLocker lock(m_console_mode_switch_lock);
  238. VERIFY(m_framebuffer_console);
  239. VERIFY(m_framebuffer_device);
  240. m_console_enabled = false;
  241. m_registers->bochs_regs.y_offset = 0;
  242. m_framebuffer_console->disable();
  243. m_framebuffer_device->activate_writes();
  244. }
  245. ErrorOr<ByteBuffer> BochsGraphicsAdapter::get_edid(size_t output_port_index) const
  246. {
  247. if (output_port_index != 0)
  248. return Error::from_errno(ENODEV);
  249. return ByteBuffer::copy(const_cast<u8 const*>(m_registers->edid_data), sizeof(m_registers->edid_data));
  250. }
  251. }