GraphicsManagement.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  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/VGA/ISAAdapter.h>
  16. #include <Kernel/Graphics/VGA/PCIAdapter.h>
  17. #include <Kernel/Graphics/VGA/VGACompatibleAdapter.h>
  18. #include <Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h>
  19. #include <Kernel/Memory/AnonymousVMObject.h>
  20. #include <Kernel/Multiboot.h>
  21. #include <Kernel/Sections.h>
  22. namespace Kernel {
  23. static Singleton<GraphicsManagement> s_the;
  24. extern Atomic<Graphics::Console*> g_boot_console;
  25. GraphicsManagement& GraphicsManagement::the()
  26. {
  27. return *s_the;
  28. }
  29. bool GraphicsManagement::is_initialized()
  30. {
  31. return s_the.is_initialized();
  32. }
  33. UNMAP_AFTER_INIT GraphicsManagement::GraphicsManagement()
  34. {
  35. }
  36. bool GraphicsManagement::framebuffer_devices_use_bootloader_framebuffer() const
  37. {
  38. return kernel_command_line().are_framebuffer_devices_enabled() == CommandLine::FrameBufferDevices::BootloaderOnly;
  39. }
  40. bool GraphicsManagement::framebuffer_devices_console_only() const
  41. {
  42. return kernel_command_line().are_framebuffer_devices_enabled() == CommandLine::FrameBufferDevices::ConsoleOnly;
  43. }
  44. void GraphicsManagement::disable_vga_emulation_access_permanently()
  45. {
  46. SpinlockLocker locker(m_main_vga_lock);
  47. disable_vga_text_mode_console_cursor();
  48. IO::out8(0x3c4, 1);
  49. u8 sr1 = IO::in8(0x3c5);
  50. IO::out8(0x3c5, sr1 | 1 << 5);
  51. IO::delay(1000);
  52. m_vga_access_is_disabled = true;
  53. }
  54. void GraphicsManagement::enable_vga_text_mode_console_cursor()
  55. {
  56. SpinlockLocker locker(m_main_vga_lock);
  57. if (m_vga_access_is_disabled)
  58. return;
  59. IO::out8(0x3D4, 0xA);
  60. IO::out8(0x3D5, 0);
  61. }
  62. void GraphicsManagement::disable_vga_text_mode_console_cursor()
  63. {
  64. SpinlockLocker locker(m_main_vga_lock);
  65. if (m_vga_access_is_disabled)
  66. return;
  67. IO::out8(0x3D4, 0xA);
  68. IO::out8(0x3D5, 0x20);
  69. }
  70. void GraphicsManagement::set_vga_text_mode_cursor(size_t console_width, size_t x, size_t y)
  71. {
  72. SpinlockLocker locker(m_main_vga_lock);
  73. if (m_vga_access_is_disabled)
  74. return;
  75. enable_vga_text_mode_console_cursor();
  76. u16 value = y * console_width + x;
  77. IO::out8(0x3d4, 0x0e);
  78. IO::out8(0x3d5, MSB(value));
  79. IO::out8(0x3d4, 0x0f);
  80. IO::out8(0x3d5, LSB(value));
  81. }
  82. void GraphicsManagement::deactivate_graphical_mode()
  83. {
  84. // FIXME: Remove this once we don't support Framebuffer devices anymore.
  85. for (auto& graphics_device : m_graphics_devices) {
  86. graphics_device.enable_consoles();
  87. }
  88. for (auto& connector : m_display_connector_nodes) {
  89. connector.set_display_mode({}, DisplayConnector::DisplayMode::Console);
  90. }
  91. }
  92. void GraphicsManagement::activate_graphical_mode()
  93. {
  94. // FIXME: Remove this once we don't support Framebuffer devices anymore.
  95. for (auto& graphics_device : m_graphics_devices) {
  96. graphics_device.disable_consoles();
  97. }
  98. for (auto& connector : m_display_connector_nodes) {
  99. connector.set_display_mode({}, DisplayConnector::DisplayMode::Graphical);
  100. }
  101. }
  102. void GraphicsManagement::attach_new_display_connector(Badge<DisplayConnector>, DisplayConnector& connector)
  103. {
  104. m_display_connector_nodes.append(connector);
  105. }
  106. void GraphicsManagement::detach_display_connector(Badge<DisplayConnector>, DisplayConnector& connector)
  107. {
  108. m_display_connector_nodes.remove(connector);
  109. }
  110. static inline bool is_vga_compatible_pci_device(PCI::DeviceIdentifier const& device_identifier)
  111. {
  112. // Note: Check for Display Controller, VGA Compatible Controller or
  113. // Unclassified, VGA-Compatible Unclassified Device
  114. auto is_display_controller_vga_compatible = device_identifier.class_code().value() == 0x3 && device_identifier.subclass_code().value() == 0x0;
  115. auto is_general_pci_vga_compatible = device_identifier.class_code().value() == 0x0 && device_identifier.subclass_code().value() == 0x1;
  116. return is_display_controller_vga_compatible || is_general_pci_vga_compatible;
  117. }
  118. static inline bool is_display_controller_pci_device(PCI::DeviceIdentifier const& device_identifier)
  119. {
  120. return device_identifier.class_code().value() == 0x3;
  121. }
  122. UNMAP_AFTER_INIT bool GraphicsManagement::determine_and_initialize_isa_graphics_device()
  123. {
  124. dmesgln("Graphics: Using a ISA VGA compatible generic adapter");
  125. auto adapter = ISAVGAAdapter::initialize();
  126. m_graphics_devices.append(*adapter);
  127. adapter->enable_consoles();
  128. m_vga_adapter = adapter;
  129. return true;
  130. }
  131. UNMAP_AFTER_INIT bool GraphicsManagement::determine_and_initialize_graphics_device(PCI::DeviceIdentifier const& device_identifier)
  132. {
  133. VERIFY(is_vga_compatible_pci_device(device_identifier) || is_display_controller_pci_device(device_identifier));
  134. auto add_and_configure_adapter = [&](GenericGraphicsAdapter& graphics_device) {
  135. m_graphics_devices.append(graphics_device);
  136. if (framebuffer_devices_console_only()) {
  137. graphics_device.enable_consoles();
  138. return;
  139. }
  140. graphics_device.initialize_framebuffer_devices();
  141. };
  142. RefPtr<GenericGraphicsAdapter> adapter;
  143. auto create_bootloader_framebuffer_device = [&]() {
  144. if (multiboot_framebuffer_addr.is_null()) {
  145. // Prekernel sets the framebuffer address to 0 if MULTIBOOT_INFO_FRAMEBUFFER_INFO
  146. // is not present, as there is likely never a valid framebuffer at this physical address.
  147. dmesgln("Graphics: Bootloader did not set up a framebuffer, ignoring fbdev argument");
  148. } else if (multiboot_framebuffer_type != MULTIBOOT_FRAMEBUFFER_TYPE_RGB) {
  149. dmesgln("Graphics: The framebuffer set up by the bootloader is not RGB, ignoring fbdev argument");
  150. } else {
  151. dmesgln("Graphics: Using a preset resolution from the bootloader");
  152. adapter = PCIVGACompatibleAdapter::initialize_with_preset_resolution(device_identifier,
  153. multiboot_framebuffer_addr,
  154. multiboot_framebuffer_width,
  155. multiboot_framebuffer_height,
  156. multiboot_framebuffer_pitch);
  157. }
  158. };
  159. if (framebuffer_devices_use_bootloader_framebuffer())
  160. create_bootloader_framebuffer_device();
  161. if (!adapter) {
  162. switch (device_identifier.hardware_id().vendor_id) {
  163. case PCI::VendorID::QEMUOld:
  164. if (device_identifier.hardware_id().device_id == 0x1111)
  165. adapter = BochsGraphicsAdapter::initialize(device_identifier);
  166. break;
  167. case PCI::VendorID::VirtualBox:
  168. if (device_identifier.hardware_id().device_id == 0xbeef)
  169. adapter = BochsGraphicsAdapter::initialize(device_identifier);
  170. break;
  171. case PCI::VendorID::Intel:
  172. adapter = IntelNativeGraphicsAdapter::initialize(device_identifier);
  173. break;
  174. case PCI::VendorID::VirtIO:
  175. dmesgln("Graphics: Using VirtIO console");
  176. adapter = VirtIOGraphicsAdapter::initialize(device_identifier);
  177. break;
  178. default:
  179. if (!is_vga_compatible_pci_device(device_identifier))
  180. break;
  181. // Note: Although technically possible that a system has a
  182. // non-compatible VGA graphics device that was initialized by the
  183. // Multiboot bootloader to provide a framebuffer, in practice we
  184. // probably want to support these devices natively instead of
  185. // initializing them as some sort of a generic GenericGraphicsAdapter. For now,
  186. // the only known example of this sort of device is qxl in QEMU. For VGA
  187. // compatible devices we don't have a special driver for (e.g. ati-vga,
  188. // qxl-vga, cirrus-vga, vmware-svga in QEMU), it's much more likely that
  189. // these devices will be supported by the Multiboot loader that will
  190. // utilize VESA BIOS extensions (that we don't currently) of these cards
  191. // support, so we want to utilize the provided framebuffer of these
  192. // devices, if possible.
  193. if (!m_vga_adapter && PCI::is_io_space_enabled(device_identifier.address())) {
  194. create_bootloader_framebuffer_device();
  195. } else {
  196. dmesgln("Graphics: Using a PCI VGA compatible generic adapter");
  197. adapter = PCIVGACompatibleAdapter::initialize(device_identifier);
  198. }
  199. break;
  200. }
  201. }
  202. if (!adapter)
  203. return false;
  204. add_and_configure_adapter(*adapter);
  205. // Note: If IO space is enabled, this VGA adapter is operating in VGA mode.
  206. // Note: If no other VGA adapter is attached as m_vga_adapter, we should attach it then.
  207. if (!m_vga_adapter && PCI::is_io_space_enabled(device_identifier.address()) && adapter->vga_compatible()) {
  208. dbgln("Graphics adapter @ {} is operating in VGA mode", device_identifier.address());
  209. m_vga_adapter = static_ptr_cast<VGACompatibleAdapter>(adapter);
  210. }
  211. return true;
  212. }
  213. UNMAP_AFTER_INIT bool GraphicsManagement::initialize()
  214. {
  215. /* Explanation on the flow when not requesting to force not creating any
  216. * framebuffer devices:
  217. * If the user wants to use a Console instead of the graphical environment,
  218. * they doesn't need to request text mode.
  219. * Graphical mode might not be accessible on bare-metal hardware because
  220. * the bootloader didn't set a framebuffer and we don't have a native driver
  221. * to set a framebuffer for it. We don't have VBE modesetting capabilities
  222. * in the kernel yet, so what will happen is one of the following situations:
  223. * 1. The bootloader didn't specify settings of a pre-set framebuffer. The
  224. * kernel has a native driver for a detected display adapter, therefore
  225. * the kernel can still set a framebuffer.
  226. * 2. The bootloader specified settings of a pre-set framebuffer, and the
  227. * kernel has a native driver for a detected display adapter, therefore
  228. * the kernel can still set a framebuffer and change the settings of it.
  229. * In that situation, the kernel will simply ignore the Multiboot pre-set
  230. * framebuffer.
  231. * 2. The bootloader specified settings of a pre-set framebuffer, and the
  232. * kernel does not have a native driver for a detected display adapter,
  233. * therefore the kernel will use the pre-set framebuffer. Modesetting is not
  234. * available in this situation.
  235. * 3. The bootloader didn't specify settings of a pre-set framebuffer, and
  236. * the kernel does not have a native driver for a detected display adapter,
  237. * therefore the kernel will try to initialize a VGA text mode console.
  238. * In that situation, the kernel will assume that VGA text mode was already
  239. * initialized, but will still try to modeset it. No switching to graphical
  240. * environment is allowed in this case.
  241. *
  242. * By default, the kernel assumes that no framebuffer was created until it
  243. * was proven that there's an existing framebuffer or we can modeset the
  244. * screen resolution to create a framebuffer.
  245. *
  246. * Special cases:
  247. * 1. If the user disabled PCI access, the kernel behaves like it's running
  248. * on a pure ISA PC machine and therefore the kernel will try to initialize
  249. * a variant that is suitable for ISA VGA handling, and not PCI adapters.
  250. *
  251. * If the user requests to force no initialization of framebuffer devices
  252. * the same flow above will happen, except that no framebuffer device will
  253. * be created, so SystemServer will not try to initialize WindowServer.
  254. */
  255. if (PCI::Access::is_disabled()) {
  256. determine_and_initialize_isa_graphics_device();
  257. return true;
  258. }
  259. if (framebuffer_devices_console_only())
  260. dbgln("Forcing non-initialization of framebuffer devices (console only)");
  261. else if (framebuffer_devices_use_bootloader_framebuffer())
  262. dbgln("Forcing use of framebuffer set up by the bootloader");
  263. MUST(PCI::enumerate([&](PCI::DeviceIdentifier const& device_identifier) {
  264. // Note: Each graphics controller will try to set its native screen resolution
  265. // upon creation. Later on, if we don't want to have framebuffer devices, a
  266. // framebuffer console will take the control instead.
  267. if (!is_vga_compatible_pci_device(device_identifier) && !is_display_controller_pci_device(device_identifier))
  268. return;
  269. determine_and_initialize_graphics_device(device_identifier);
  270. }));
  271. if (!m_console) {
  272. // If no graphics driver was instantiated and we had a bootloader provided
  273. // framebuffer console we can simply re-use it.
  274. if (auto* boot_console = g_boot_console.load()) {
  275. m_console = *boot_console;
  276. boot_console->unref(); // Drop the leaked reference from Kernel::init()
  277. }
  278. }
  279. if (m_graphics_devices.is_empty()) {
  280. dbgln("No graphics adapter was initialized.");
  281. return false;
  282. }
  283. return true;
  284. }
  285. bool GraphicsManagement::framebuffer_devices_exist() const
  286. {
  287. for (auto& graphics_device : m_graphics_devices) {
  288. if (graphics_device.framebuffer_devices_initialized())
  289. return true;
  290. }
  291. return false;
  292. }
  293. void GraphicsManagement::set_console(Graphics::Console& console)
  294. {
  295. m_console = console;
  296. if (auto* boot_console = g_boot_console.exchange(nullptr)) {
  297. // Disable the initial boot framebuffer console permanently
  298. boot_console->disable();
  299. // TODO: Even though we swapped the pointer and disabled the console
  300. // we technically can't safely destroy it as other CPUs might still
  301. // try to use it. Once we solve this problem we can drop the reference
  302. // that we intentionally leaked in Kernel::init().
  303. }
  304. }
  305. }