GraphicsManagement.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  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/Bus/PCI/IDs.h>
  8. #include <Kernel/CommandLine.h>
  9. #include <Kernel/Graphics/Bochs/GraphicsAdapter.h>
  10. #include <Kernel/Graphics/GraphicsManagement.h>
  11. #include <Kernel/Graphics/Intel/NativeGraphicsAdapter.h>
  12. #include <Kernel/Graphics/VGACompatibleAdapter.h>
  13. #include <Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h>
  14. #include <Kernel/IO.h>
  15. #include <Kernel/Multiboot.h>
  16. #include <Kernel/Sections.h>
  17. #include <Kernel/VM/AnonymousVMObject.h>
  18. namespace Kernel {
  19. static AK::Singleton<GraphicsManagement> s_the;
  20. GraphicsManagement& GraphicsManagement::the()
  21. {
  22. return *s_the;
  23. }
  24. bool GraphicsManagement::is_initialized()
  25. {
  26. return s_the.is_initialized();
  27. }
  28. UNMAP_AFTER_INIT GraphicsManagement::GraphicsManagement()
  29. : m_vga_font_region(MM.allocate_kernel_region(PAGE_SIZE, "VGA font", Region::Access::Read | Region::Access::Write, AllocationStrategy::AllocateNow).release_nonnull())
  30. , m_framebuffer_devices_allowed(!kernel_command_line().is_no_framebuffer_devices_mode())
  31. {
  32. }
  33. void GraphicsManagement::deactivate_graphical_mode()
  34. {
  35. for (auto& graphics_device : m_graphics_devices) {
  36. graphics_device.enable_consoles();
  37. }
  38. }
  39. void GraphicsManagement::activate_graphical_mode()
  40. {
  41. for (auto& graphics_device : m_graphics_devices) {
  42. graphics_device.disable_consoles();
  43. }
  44. }
  45. static inline bool is_vga_compatible_pci_device(PCI::Address address)
  46. {
  47. // Note: Check for Display Controller, VGA Compatible Controller or
  48. // Unclassified, VGA-Compatible Unclassified Device
  49. auto is_display_controller_vga_compatible = PCI::get_class(address) == 0x3 && PCI::get_subclass(address) == 0x0;
  50. auto is_general_pci_vga_compatible = PCI::get_class(address) == 0x0 && PCI::get_subclass(address) == 0x1;
  51. return is_display_controller_vga_compatible || is_general_pci_vga_compatible;
  52. }
  53. static inline bool is_display_controller_pci_device(PCI::Address address)
  54. {
  55. return PCI::get_class(address) == 0x3;
  56. }
  57. UNMAP_AFTER_INIT bool GraphicsManagement::determine_and_initialize_graphics_device(const PCI::Address& address, PCI::ID id)
  58. {
  59. VERIFY(is_vga_compatible_pci_device(address) || is_display_controller_pci_device(address));
  60. auto add_and_configure_adapter = [&](GraphicsDevice& graphics_device) {
  61. m_graphics_devices.append(graphics_device);
  62. if (!m_framebuffer_devices_allowed) {
  63. graphics_device.enable_consoles();
  64. return;
  65. }
  66. graphics_device.initialize_framebuffer_devices();
  67. };
  68. RefPtr<GraphicsDevice> adapter;
  69. switch (id.vendor_id) {
  70. case PCI::VendorID::QEMUOld:
  71. if (id.device_id == 0x1111)
  72. adapter = BochsGraphicsAdapter::initialize(address);
  73. break;
  74. case PCI::VendorID::VirtualBox:
  75. if (id.device_id == 0xbeef)
  76. adapter = BochsGraphicsAdapter::initialize(address);
  77. break;
  78. case PCI::VendorID::Intel:
  79. adapter = IntelNativeGraphicsAdapter::initialize(address);
  80. break;
  81. case PCI::VendorID::VirtIO:
  82. dmesgln("Graphics: Using VirtIO console");
  83. adapter = Graphics::VirtIOGPU::GraphicsAdapter::initialize(address);
  84. break;
  85. default:
  86. if (!is_vga_compatible_pci_device(address))
  87. break;
  88. // Note: Although technically possible that a system has a
  89. // non-compatible VGA graphics device that was initialized by the
  90. // Multiboot bootloader to provide a framebuffer, in practice we
  91. // probably want to support these devices natively instead of
  92. // initializing them as some sort of a generic GraphicsDevice. For now,
  93. // the only known example of this sort of device is qxl in QEMU. For VGA
  94. // compatible devices we don't have a special driver for (e.g. ati-vga,
  95. // qxl-vga, cirrus-vga, vmware-svga in QEMU), it's much more likely that
  96. // these devices will be supported by the Multiboot loader that will
  97. // utilize VESA BIOS extensions (that we don't currently) of these cards
  98. // support, so we want to utilize the provided framebuffer of these
  99. // devices, if possible.
  100. if (!m_vga_adapter && PCI::is_io_space_enabled(address)) {
  101. if (multiboot_framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB) {
  102. dmesgln("Graphics: Using a preset resolution from the bootloader");
  103. adapter = VGACompatibleAdapter::initialize_with_preset_resolution(address,
  104. multiboot_framebuffer_addr,
  105. multiboot_framebuffer_width,
  106. multiboot_framebuffer_height,
  107. multiboot_framebuffer_pitch);
  108. }
  109. } else {
  110. dmesgln("Graphics: Using a VGA compatible generic adapter");
  111. adapter = VGACompatibleAdapter::initialize(address);
  112. }
  113. break;
  114. }
  115. if (!adapter)
  116. return false;
  117. add_and_configure_adapter(*adapter);
  118. // Note: If IO space is enabled, this VGA adapter is operating in VGA mode.
  119. // Note: If no other VGA adapter is attached as m_vga_adapter, we should attach it then.
  120. if (!m_vga_adapter && PCI::is_io_space_enabled(address) && adapter->type() == GraphicsDevice::Type::VGACompatible) {
  121. dbgln("Graphics adapter @ {} is operating in VGA mode", address);
  122. m_vga_adapter = adapter;
  123. }
  124. return true;
  125. }
  126. UNMAP_AFTER_INIT bool GraphicsManagement::initialize()
  127. {
  128. /* Explanation on the flow when not requesting to force not creating any
  129. * framebuffer devices:
  130. * If the user wants to use a Console instead of the graphical environment,
  131. * they doesn't need to request text mode.
  132. * Graphical mode might not be accessible on bare-metal hardware because
  133. * the bootloader didn't set a framebuffer and we don't have a native driver
  134. * to set a framebuffer for it. We don't have VBE modesetting capabilities
  135. * in the kernel yet, so what will happen is one of the following situations:
  136. * 1. The bootloader didn't specify settings of a pre-set framebuffer. The
  137. * kernel has a native driver for a detected display adapter, therefore
  138. * the kernel can still set a framebuffer.
  139. * 2. The bootloader specified settings of a pre-set framebuffer, and the
  140. * kernel has a native driver for a detected display adapter, therefore
  141. * the kernel can still set a framebuffer and change the settings of it.
  142. * In that situation, the kernel will simply ignore the Multiboot pre-set
  143. * framebuffer.
  144. * 2. The bootloader specified settings of a pre-set framebuffer, and the
  145. * kernel does not have a native driver for a detected display adapter,
  146. * therefore the kernel will use the pre-set framebuffer. Modesetting is not
  147. * available in this situation.
  148. * 3. The bootloader didn't specify settings of a pre-set framebuffer, and
  149. * the kernel does not have a native driver for a detected display adapter,
  150. * therefore the kernel will try to initialize a VGA text mode console.
  151. * In that situation, the kernel will assume that VGA text mode was already
  152. * initialized, but will still try to modeset it. No switching to graphical
  153. * environment is allowed in this case.
  154. *
  155. * By default, the kernel assumes that no framebuffer was created until it
  156. * was proven that there's an existing framebuffer or we can modeset the
  157. * screen resolution to create a framebuffer.
  158. *
  159. * If the user requests to force no initialization of framebuffer devices
  160. * the same flow above will happen, except that no framebuffer device will
  161. * be created, so SystemServer will not try to initialize WindowServer.
  162. */
  163. if (kernel_command_line().is_no_framebuffer_devices_mode()) {
  164. dbgln("Forcing no initialization of framebuffer devices");
  165. }
  166. PCI::enumerate([&](const PCI::Address& address, PCI::ID id) {
  167. // Note: Each graphics controller will try to set its native screen resolution
  168. // upon creation. Later on, if we don't want to have framebuffer devices, a
  169. // framebuffer console will take the control instead.
  170. if (!is_vga_compatible_pci_device(address) && !is_display_controller_pci_device(address))
  171. return;
  172. determine_and_initialize_graphics_device(address, id);
  173. });
  174. if (m_graphics_devices.is_empty()) {
  175. dbgln("No graphics adapter was initialized.");
  176. return false;
  177. }
  178. return true;
  179. }
  180. bool GraphicsManagement::framebuffer_devices_exist() const
  181. {
  182. for (auto& graphics_device : m_graphics_devices) {
  183. if (graphics_device.framebuffer_devices_initialized())
  184. return true;
  185. }
  186. return false;
  187. }
  188. }