Management.cpp 11 KB

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