GraphicsManagement.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  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/x86/IO.h>
  8. #include <Kernel/Bus/PCI/API.h>
  9. #include <Kernel/Bus/PCI/IDs.h>
  10. #include <Kernel/CommandLine.h>
  11. #include <Kernel/Graphics/Bochs/GraphicsAdapter.h>
  12. #include <Kernel/Graphics/Console/BootFramebufferConsole.h>
  13. #include <Kernel/Graphics/GraphicsManagement.h>
  14. #include <Kernel/Graphics/Intel/NativeGraphicsAdapter.h>
  15. #include <Kernel/Graphics/VGACompatibleAdapter.h>
  16. #include <Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h>
  17. #include <Kernel/Memory/AnonymousVMObject.h>
  18. #include <Kernel/Multiboot.h>
  19. #include <Kernel/Sections.h>
  20. namespace Kernel {
  21. static Singleton<GraphicsManagement> s_the;
  22. extern Atomic<Graphics::Console*> g_boot_console;
  23. GraphicsManagement& GraphicsManagement::the()
  24. {
  25. return *s_the;
  26. }
  27. bool GraphicsManagement::is_initialized()
  28. {
  29. return s_the.is_initialized();
  30. }
  31. UNMAP_AFTER_INIT GraphicsManagement::GraphicsManagement()
  32. {
  33. }
  34. bool GraphicsManagement::framebuffer_devices_use_bootloader_framebuffer() const
  35. {
  36. return kernel_command_line().are_framebuffer_devices_enabled() == CommandLine::FrameBufferDevices::BootloaderOnly;
  37. }
  38. bool GraphicsManagement::framebuffer_devices_console_only() const
  39. {
  40. return kernel_command_line().are_framebuffer_devices_enabled() == CommandLine::FrameBufferDevices::ConsoleOnly;
  41. }
  42. void GraphicsManagement::deactivate_graphical_mode()
  43. {
  44. for (auto& graphics_device : m_graphics_devices) {
  45. graphics_device.enable_consoles();
  46. }
  47. }
  48. void GraphicsManagement::activate_graphical_mode()
  49. {
  50. for (auto& graphics_device : m_graphics_devices) {
  51. graphics_device.disable_consoles();
  52. }
  53. }
  54. static inline bool is_vga_compatible_pci_device(PCI::DeviceIdentifier const& device_identifier)
  55. {
  56. // Note: Check for Display Controller, VGA Compatible Controller or
  57. // Unclassified, VGA-Compatible Unclassified Device
  58. auto is_display_controller_vga_compatible = device_identifier.class_code().value() == 0x3 && device_identifier.subclass_code().value() == 0x0;
  59. auto is_general_pci_vga_compatible = device_identifier.class_code().value() == 0x0 && device_identifier.subclass_code().value() == 0x1;
  60. return is_display_controller_vga_compatible || is_general_pci_vga_compatible;
  61. }
  62. static inline bool is_display_controller_pci_device(PCI::DeviceIdentifier const& device_identifier)
  63. {
  64. return device_identifier.class_code().value() == 0x3;
  65. }
  66. UNMAP_AFTER_INIT bool GraphicsManagement::determine_and_initialize_graphics_device(PCI::DeviceIdentifier const& device_identifier)
  67. {
  68. VERIFY(is_vga_compatible_pci_device(device_identifier) || is_display_controller_pci_device(device_identifier));
  69. auto add_and_configure_adapter = [&](GenericGraphicsAdapter& graphics_device) {
  70. m_graphics_devices.append(graphics_device);
  71. if (framebuffer_devices_console_only()) {
  72. graphics_device.enable_consoles();
  73. return;
  74. }
  75. graphics_device.initialize_framebuffer_devices();
  76. };
  77. RefPtr<GenericGraphicsAdapter> adapter;
  78. auto create_bootloader_framebuffer_device = [&]() {
  79. if (multiboot_framebuffer_addr.is_null()) {
  80. // Prekernel sets the framebuffer address to 0 if MULTIBOOT_INFO_FRAMEBUFFER_INFO
  81. // is not present, as there is likely never a valid framebuffer at this physical address.
  82. dmesgln("Graphics: Bootloader did not set up a framebuffer, ignoring fbdev argument");
  83. } else if (multiboot_framebuffer_type != MULTIBOOT_FRAMEBUFFER_TYPE_RGB) {
  84. dmesgln("Graphics: The framebuffer set up by the bootloader is not RGB, ignoring fbdev argument");
  85. } else {
  86. dmesgln("Graphics: Using a preset resolution from the bootloader");
  87. adapter = VGACompatibleAdapter::initialize_with_preset_resolution(device_identifier,
  88. multiboot_framebuffer_addr,
  89. multiboot_framebuffer_width,
  90. multiboot_framebuffer_height,
  91. multiboot_framebuffer_pitch);
  92. }
  93. };
  94. if (framebuffer_devices_use_bootloader_framebuffer())
  95. create_bootloader_framebuffer_device();
  96. if (!adapter) {
  97. switch (device_identifier.hardware_id().vendor_id) {
  98. case PCI::VendorID::QEMUOld:
  99. if (device_identifier.hardware_id().device_id == 0x1111)
  100. adapter = BochsGraphicsAdapter::initialize(device_identifier);
  101. break;
  102. case PCI::VendorID::VirtualBox:
  103. if (device_identifier.hardware_id().device_id == 0xbeef)
  104. adapter = BochsGraphicsAdapter::initialize(device_identifier);
  105. break;
  106. case PCI::VendorID::Intel:
  107. adapter = IntelNativeGraphicsAdapter::initialize(device_identifier);
  108. break;
  109. case PCI::VendorID::VirtIO:
  110. dmesgln("Graphics: Using VirtIO console");
  111. adapter = Graphics::VirtIOGPU::GraphicsAdapter::initialize(device_identifier);
  112. break;
  113. default:
  114. if (!is_vga_compatible_pci_device(device_identifier))
  115. break;
  116. // Note: Although technically possible that a system has a
  117. // non-compatible VGA graphics device that was initialized by the
  118. // Multiboot bootloader to provide a framebuffer, in practice we
  119. // probably want to support these devices natively instead of
  120. // initializing them as some sort of a generic GenericGraphicsAdapter. For now,
  121. // the only known example of this sort of device is qxl in QEMU. For VGA
  122. // compatible devices we don't have a special driver for (e.g. ati-vga,
  123. // qxl-vga, cirrus-vga, vmware-svga in QEMU), it's much more likely that
  124. // these devices will be supported by the Multiboot loader that will
  125. // utilize VESA BIOS extensions (that we don't currently) of these cards
  126. // support, so we want to utilize the provided framebuffer of these
  127. // devices, if possible.
  128. if (!m_vga_adapter && PCI::is_io_space_enabled(device_identifier.address())) {
  129. create_bootloader_framebuffer_device();
  130. } else {
  131. dmesgln("Graphics: Using a VGA compatible generic adapter");
  132. adapter = VGACompatibleAdapter::initialize(device_identifier);
  133. }
  134. break;
  135. }
  136. }
  137. if (!adapter)
  138. return false;
  139. add_and_configure_adapter(*adapter);
  140. // Note: If IO space is enabled, this VGA adapter is operating in VGA mode.
  141. // Note: If no other VGA adapter is attached as m_vga_adapter, we should attach it then.
  142. if (!m_vga_adapter && PCI::is_io_space_enabled(device_identifier.address()) && adapter->vga_compatible()) {
  143. dbgln("Graphics adapter @ {} is operating in VGA mode", device_identifier.address());
  144. m_vga_adapter = static_ptr_cast<VGACompatibleAdapter>(adapter);
  145. }
  146. return true;
  147. }
  148. UNMAP_AFTER_INIT bool GraphicsManagement::initialize()
  149. {
  150. /* Explanation on the flow when not requesting to force not creating any
  151. * framebuffer devices:
  152. * If the user wants to use a Console instead of the graphical environment,
  153. * they doesn't need to request text mode.
  154. * Graphical mode might not be accessible on bare-metal hardware because
  155. * the bootloader didn't set a framebuffer and we don't have a native driver
  156. * to set a framebuffer for it. We don't have VBE modesetting capabilities
  157. * in the kernel yet, so what will happen is one of the following situations:
  158. * 1. The bootloader didn't specify settings of a pre-set framebuffer. The
  159. * kernel has a native driver for a detected display adapter, therefore
  160. * the kernel can still set a framebuffer.
  161. * 2. The bootloader specified settings of a pre-set framebuffer, and the
  162. * kernel has a native driver for a detected display adapter, therefore
  163. * the kernel can still set a framebuffer and change the settings of it.
  164. * In that situation, the kernel will simply ignore the Multiboot pre-set
  165. * framebuffer.
  166. * 2. The bootloader specified settings of a pre-set framebuffer, and the
  167. * kernel does not have a native driver for a detected display adapter,
  168. * therefore the kernel will use the pre-set framebuffer. Modesetting is not
  169. * available in this situation.
  170. * 3. The bootloader didn't specify settings of a pre-set framebuffer, and
  171. * the kernel does not have a native driver for a detected display adapter,
  172. * therefore the kernel will try to initialize a VGA text mode console.
  173. * In that situation, the kernel will assume that VGA text mode was already
  174. * initialized, but will still try to modeset it. No switching to graphical
  175. * environment is allowed in this case.
  176. *
  177. * By default, the kernel assumes that no framebuffer was created until it
  178. * was proven that there's an existing framebuffer or we can modeset the
  179. * screen resolution to create a framebuffer.
  180. *
  181. * If the user requests to force no initialization of framebuffer devices
  182. * the same flow above will happen, except that no framebuffer device will
  183. * be created, so SystemServer will not try to initialize WindowServer.
  184. */
  185. if (framebuffer_devices_console_only())
  186. dbgln("Forcing non-initialization of framebuffer devices (console only)");
  187. else if (framebuffer_devices_use_bootloader_framebuffer())
  188. dbgln("Forcing use of framebuffer set up by the bootloader");
  189. PCI::enumerate([&](PCI::DeviceIdentifier const& device_identifier) {
  190. // Note: Each graphics controller will try to set its native screen resolution
  191. // upon creation. Later on, if we don't want to have framebuffer devices, a
  192. // framebuffer console will take the control instead.
  193. if (!is_vga_compatible_pci_device(device_identifier) && !is_display_controller_pci_device(device_identifier))
  194. return;
  195. determine_and_initialize_graphics_device(device_identifier);
  196. });
  197. if (!m_console) {
  198. // If no graphics driver was instantiated and we had a bootloader provided
  199. // framebuffer console we can simply re-use it.
  200. if (auto* boot_console = g_boot_console.load()) {
  201. m_console = *boot_console;
  202. boot_console->unref(); // Drop the leaked reference from Kernel::init()
  203. }
  204. }
  205. if (m_graphics_devices.is_empty()) {
  206. dbgln("No graphics adapter was initialized.");
  207. return false;
  208. }
  209. return true;
  210. }
  211. bool GraphicsManagement::framebuffer_devices_exist() const
  212. {
  213. for (auto& graphics_device : m_graphics_devices) {
  214. if (graphics_device.framebuffer_devices_initialized())
  215. return true;
  216. }
  217. return false;
  218. }
  219. void GraphicsManagement::set_console(Graphics::Console& console)
  220. {
  221. m_console = console;
  222. if (auto* boot_console = g_boot_console.exchange(nullptr)) {
  223. // Disable the initial boot framebuffer console permanently
  224. boot_console->disable();
  225. // TODO: Even though we swapped the pointer and disabled the console
  226. // we technically can't safely destroy it as other CPUs might still
  227. // try to use it. Once we solve this problem we can drop the reference
  228. // that we intentionally leaked in Kernel::init().
  229. }
  230. }
  231. }