GraphicsManagement.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. /*
  2. * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Singleton.h>
  7. #include <Kernel/Arch/Delay.h>
  8. #include <Kernel/Arch/x86/IO.h>
  9. #if ARCH(I386) || ARCH(X86_64)
  10. # include <Kernel/Arch/x86/Hypervisor/BochsDisplayConnector.h>
  11. #endif
  12. #include <Kernel/Bus/PCI/API.h>
  13. #include <Kernel/Bus/PCI/IDs.h>
  14. #include <Kernel/CommandLine.h>
  15. #include <Kernel/Graphics/Bochs/GraphicsAdapter.h>
  16. #include <Kernel/Graphics/Console/BootFramebufferConsole.h>
  17. #include <Kernel/Graphics/GraphicsManagement.h>
  18. #include <Kernel/Graphics/Intel/NativeGraphicsAdapter.h>
  19. #include <Kernel/Graphics/VMWare/GraphicsAdapter.h>
  20. #include <Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h>
  21. #include <Kernel/Memory/AnonymousVMObject.h>
  22. #include <Kernel/Multiboot.h>
  23. #include <Kernel/Sections.h>
  24. namespace Kernel {
  25. static Singleton<GraphicsManagement> s_the;
  26. extern Atomic<Graphics::Console*> g_boot_console;
  27. GraphicsManagement& GraphicsManagement::the()
  28. {
  29. return *s_the;
  30. }
  31. bool GraphicsManagement::is_initialized()
  32. {
  33. return s_the.is_initialized();
  34. }
  35. UNMAP_AFTER_INIT GraphicsManagement::GraphicsManagement()
  36. {
  37. }
  38. void GraphicsManagement::disable_vga_emulation_access_permanently()
  39. {
  40. SpinlockLocker locker(m_main_vga_lock);
  41. disable_vga_text_mode_console_cursor();
  42. IO::out8(0x3c4, 1);
  43. u8 sr1 = IO::in8(0x3c5);
  44. IO::out8(0x3c5, sr1 | 1 << 5);
  45. microseconds_delay(1000);
  46. m_vga_access_is_disabled = true;
  47. }
  48. void GraphicsManagement::enable_vga_text_mode_console_cursor()
  49. {
  50. SpinlockLocker locker(m_main_vga_lock);
  51. if (m_vga_access_is_disabled)
  52. return;
  53. IO::out8(0x3D4, 0xA);
  54. IO::out8(0x3D5, 0);
  55. }
  56. void GraphicsManagement::disable_vga_text_mode_console_cursor()
  57. {
  58. SpinlockLocker locker(m_main_vga_lock);
  59. if (m_vga_access_is_disabled)
  60. return;
  61. IO::out8(0x3D4, 0xA);
  62. IO::out8(0x3D5, 0x20);
  63. }
  64. void GraphicsManagement::set_vga_text_mode_cursor(size_t console_width, size_t x, size_t y)
  65. {
  66. SpinlockLocker locker(m_main_vga_lock);
  67. if (m_vga_access_is_disabled)
  68. return;
  69. enable_vga_text_mode_console_cursor();
  70. u16 value = y * console_width + x;
  71. IO::out8(0x3d4, 0x0e);
  72. IO::out8(0x3d5, MSB(value));
  73. IO::out8(0x3d4, 0x0f);
  74. IO::out8(0x3d5, LSB(value));
  75. }
  76. void GraphicsManagement::deactivate_graphical_mode()
  77. {
  78. return m_display_connector_nodes.with([&](auto& display_connectors) {
  79. for (auto& connector : display_connectors)
  80. connector.set_display_mode({}, DisplayConnector::DisplayMode::Console);
  81. });
  82. }
  83. void GraphicsManagement::activate_graphical_mode()
  84. {
  85. return m_display_connector_nodes.with([&](auto& display_connectors) {
  86. for (auto& connector : display_connectors)
  87. connector.set_display_mode({}, DisplayConnector::DisplayMode::Graphical);
  88. });
  89. }
  90. void GraphicsManagement::attach_new_display_connector(Badge<DisplayConnector>, DisplayConnector& connector)
  91. {
  92. return m_display_connector_nodes.with([&](auto& display_connectors) {
  93. display_connectors.append(connector);
  94. });
  95. }
  96. void GraphicsManagement::detach_display_connector(Badge<DisplayConnector>, DisplayConnector& connector)
  97. {
  98. return m_display_connector_nodes.with([&](auto& display_connectors) {
  99. display_connectors.remove(connector);
  100. });
  101. }
  102. static inline bool is_vga_compatible_pci_device(PCI::DeviceIdentifier const& device_identifier)
  103. {
  104. // Note: Check for Display Controller, VGA Compatible Controller or
  105. // Unclassified, VGA-Compatible Unclassified Device
  106. auto is_display_controller_vga_compatible = device_identifier.class_code().value() == 0x3 && device_identifier.subclass_code().value() == 0x0;
  107. auto is_general_pci_vga_compatible = device_identifier.class_code().value() == 0x0 && device_identifier.subclass_code().value() == 0x1;
  108. return is_display_controller_vga_compatible || is_general_pci_vga_compatible;
  109. }
  110. static inline bool is_display_controller_pci_device(PCI::DeviceIdentifier const& device_identifier)
  111. {
  112. return device_identifier.class_code().value() == 0x3;
  113. }
  114. UNMAP_AFTER_INIT bool GraphicsManagement::determine_and_initialize_graphics_device(PCI::DeviceIdentifier const& device_identifier)
  115. {
  116. VERIFY(is_vga_compatible_pci_device(device_identifier) || is_display_controller_pci_device(device_identifier));
  117. LockRefPtr<GenericGraphicsAdapter> adapter;
  118. if (!adapter) {
  119. switch (device_identifier.hardware_id().vendor_id) {
  120. case PCI::VendorID::QEMUOld:
  121. if (device_identifier.hardware_id().device_id == 0x1111)
  122. adapter = BochsGraphicsAdapter::initialize(device_identifier);
  123. break;
  124. case PCI::VendorID::VirtualBox:
  125. if (device_identifier.hardware_id().device_id == 0xbeef)
  126. adapter = BochsGraphicsAdapter::initialize(device_identifier);
  127. break;
  128. case PCI::VendorID::Intel:
  129. adapter = IntelNativeGraphicsAdapter::initialize(device_identifier);
  130. break;
  131. case PCI::VendorID::VirtIO:
  132. dmesgln("Graphics: Using VirtIO console");
  133. adapter = VirtIOGraphicsAdapter::initialize(device_identifier);
  134. break;
  135. case PCI::VendorID::VMWare:
  136. adapter = VMWareGraphicsAdapter::try_initialize(device_identifier);
  137. break;
  138. default:
  139. break;
  140. }
  141. }
  142. if (!adapter)
  143. return false;
  144. m_graphics_devices.append(*adapter);
  145. return true;
  146. }
  147. UNMAP_AFTER_INIT void GraphicsManagement::initialize_preset_resolution_generic_display_connector()
  148. {
  149. VERIFY(!multiboot_framebuffer_addr.is_null());
  150. VERIFY(multiboot_framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB);
  151. dmesgln("Graphics: Using a preset resolution from the bootloader, without knowing the PCI device");
  152. m_preset_resolution_generic_display_connector = GenericDisplayConnector::must_create_with_preset_resolution(
  153. multiboot_framebuffer_addr,
  154. multiboot_framebuffer_width,
  155. multiboot_framebuffer_height,
  156. multiboot_framebuffer_pitch);
  157. }
  158. UNMAP_AFTER_INIT bool GraphicsManagement::initialize()
  159. {
  160. /* Explanation on the flow here:
  161. *
  162. * If the user chose to disable graphics support entirely, then all we can do
  163. * is to set up a plain old VGA text console and exit this function.
  164. * Otherwise, we either try to find a device that we natively support so
  165. * we can initialize it, and in case we don't find any device to initialize,
  166. * we try to initialize a simple DisplayConnector to support a pre-initialized
  167. * framebuffer.
  168. *
  169. * Note: If the user disabled PCI access, the kernel behaves like it's running
  170. * on a pure ISA PC machine and therefore the kernel will try to initialize
  171. * a variant that is suitable for ISA VGA handling, and not PCI adapters.
  172. */
  173. ScopeGuard assign_console_on_initialization_exit([this] {
  174. if (!m_console) {
  175. // If no graphics driver was instantiated and we had a bootloader provided
  176. // framebuffer console we can simply re-use it.
  177. if (auto* boot_console = g_boot_console.load()) {
  178. m_console = *boot_console;
  179. boot_console->unref(); // Drop the leaked reference from Kernel::init()
  180. }
  181. }
  182. });
  183. auto graphics_subsystem_mode = kernel_command_line().graphics_subsystem_mode();
  184. if (graphics_subsystem_mode == CommandLine::GraphicsSubsystemMode::Disabled) {
  185. VERIFY(!m_console);
  186. return true;
  187. }
  188. // Note: Don't try to initialize an ISA Bochs VGA adapter if PCI hardware is
  189. // present but the user decided to disable its usage nevertheless.
  190. // Otherwise we risk using the Bochs VBE driver on a wrong physical address
  191. // for the framebuffer.
  192. if (PCI::Access::is_hardware_disabled() && !(graphics_subsystem_mode == CommandLine::GraphicsSubsystemMode::Limited && !multiboot_framebuffer_addr.is_null() && multiboot_framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB)) {
  193. #if ARCH(I386) || ARCH(X86_64)
  194. auto vga_isa_bochs_display_connector = BochsDisplayConnector::try_create_for_vga_isa_connector();
  195. if (vga_isa_bochs_display_connector) {
  196. dmesgln("Graphics: Using a Bochs ISA VGA compatible adapter");
  197. MUST(vga_isa_bochs_display_connector->set_safe_mode_setting());
  198. m_platform_board_specific_display_connector = vga_isa_bochs_display_connector;
  199. dmesgln("Graphics: Invoking manual blanking with VGA ISA ports");
  200. SpinlockLocker locker(m_main_vga_lock);
  201. IO::out8(0x3c0, 0x20);
  202. return true;
  203. }
  204. #endif
  205. }
  206. if (graphics_subsystem_mode == CommandLine::GraphicsSubsystemMode::Limited && !multiboot_framebuffer_addr.is_null() && multiboot_framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB) {
  207. initialize_preset_resolution_generic_display_connector();
  208. return true;
  209. }
  210. if (PCI::Access::is_disabled()) {
  211. dmesgln("Graphics: Using an assumed-to-exist ISA VGA compatible generic adapter");
  212. return true;
  213. }
  214. MUST(PCI::enumerate([&](PCI::DeviceIdentifier const& device_identifier) {
  215. // Note: Each graphics controller will try to set its native screen resolution
  216. // upon creation. Later on, if we don't want to have framebuffer devices, a
  217. // framebuffer console will take the control instead.
  218. if (!is_vga_compatible_pci_device(device_identifier) && !is_display_controller_pci_device(device_identifier))
  219. return;
  220. determine_and_initialize_graphics_device(device_identifier);
  221. }));
  222. // Note: If we failed to find any graphics device to be used natively, but the
  223. // bootloader prepared a framebuffer for us to use, then just create a DisplayConnector
  224. // for it so the user can still use the system in graphics mode.
  225. // Prekernel sets the framebuffer address to 0 if MULTIBOOT_INFO_FRAMEBUFFER_INFO
  226. // is not present, as there is likely never a valid framebuffer at this physical address.
  227. // Note: We only support RGB framebuffers. Any other format besides RGBX (and RGBA) or BGRX (and BGRA) is obsolete
  228. // and is not useful for us.
  229. if (m_graphics_devices.is_empty() && !multiboot_framebuffer_addr.is_null() && multiboot_framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB) {
  230. initialize_preset_resolution_generic_display_connector();
  231. return true;
  232. }
  233. if (m_graphics_devices.is_empty()) {
  234. dbgln("No graphics adapter was initialized.");
  235. return false;
  236. }
  237. return true;
  238. }
  239. void GraphicsManagement::set_console(Graphics::Console& console)
  240. {
  241. m_console = console;
  242. if (auto* boot_console = g_boot_console.exchange(nullptr)) {
  243. // Disable the initial boot framebuffer console permanently
  244. boot_console->disable();
  245. // TODO: Even though we swapped the pointer and disabled the console
  246. // we technically can't safely destroy it as other CPUs might still
  247. // try to use it. Once we solve this problem we can drop the reference
  248. // that we intentionally leaked in Kernel::init().
  249. }
  250. }
  251. }