From 731fc5a7c88d79a2f3392ae4d0bff99a6f104357 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Wed, 6 Feb 2019 10:17:26 +0100 Subject: [PATCH] Kernel: Much improved BochsVGA (BXVGA) support. Instead of cowboy-calling the VESA BIOS in the bootloader, find the emulator VGA adapter by scanning the PCI bus. Then set up the desired video mode by sending device commands. --- .gitignore | 3 + Kernel/.bochsrc | 2 +- Kernel/BochsVGADevice.cpp | 68 +++++++++++++++++++++ Kernel/BochsVGADevice.h | 25 ++++++++ Kernel/Boot/boot.asm | 37 ------------ Kernel/IO.h | 2 +- Kernel/Makefile | 2 + Kernel/PCI.cpp | 121 ++++++++++++++++++++++++++++++++++++++ Kernel/PCI.h | 48 +++++++++++++++ Kernel/Process.cpp | 73 ----------------------- Kernel/Process.h | 2 +- Kernel/ProcessGUI.cpp | 20 +++++++ Kernel/init.cpp | 4 ++ WindowServer/WSScreen.cpp | 1 + WindowServer/WSScreen.h | 2 + WindowServer/main.cpp | 2 +- 16 files changed, 298 insertions(+), 114 deletions(-) create mode 100644 Kernel/BochsVGADevice.cpp create mode 100644 Kernel/BochsVGADevice.h create mode 100644 Kernel/PCI.cpp create mode 100644 Kernel/PCI.h diff --git a/.gitignore b/.gitignore index 9a5a80a2e5b..4a1cdea2236 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,6 @@ *.creator.user.* *.files *.includes +*.cflags +*.cxxflags +Patches diff --git a/Kernel/.bochsrc b/Kernel/.bochsrc index ccdac5dbb96..52b78effd20 100644 --- a/Kernel/.bochsrc +++ b/Kernel/.bochsrc @@ -24,7 +24,7 @@ optramimage1: file=none optramimage2: file=none optramimage3: file=none optramimage4: file=none -pci: enabled=1, chipset=i440fx +pci: enabled=1, chipset=i440fx, slot1=pcivga vga: extension=vbe, update_freq=60, realtime=0 cpu: count=1, ips=4000000, model=bx_generic, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0 cpuid: level=6, stepping=3, model=3, family=6, vendor_string="GenuineIntel", brand_string=" Intel(R) Pentium(R) 4 CPU " diff --git a/Kernel/BochsVGADevice.cpp b/Kernel/BochsVGADevice.cpp new file mode 100644 index 00000000000..45a32a9262f --- /dev/null +++ b/Kernel/BochsVGADevice.cpp @@ -0,0 +1,68 @@ +#include +#include +#include + +#define VBE_DISPI_IOPORT_INDEX 0x01CE +#define VBE_DISPI_IOPORT_DATA 0x01CF + +#define VBE_DISPI_INDEX_ID 0x0 +#define VBE_DISPI_INDEX_XRES 0x1 +#define VBE_DISPI_INDEX_YRES 0x2 +#define VBE_DISPI_INDEX_BPP 0x3 +#define VBE_DISPI_INDEX_ENABLE 0x4 +#define VBE_DISPI_INDEX_BANK 0x5 +#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 +#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 +#define VBE_DISPI_DISABLED 0x00 +#define VBE_DISPI_ENABLED 0x01 +#define VBE_DISPI_LFB_ENABLED 0x40 + +static BochsVGADevice* s_the; + +BochsVGADevice& BochsVGADevice::the() +{ + return *s_the; +} + +void BochsVGADevice::initialize_statics() +{ + s_the = nullptr; +} + +BochsVGADevice::BochsVGADevice() +{ + s_the = this; + m_framebuffer_address = PhysicalAddress(find_framebuffer_address()); +} + +void BochsVGADevice::set_register(word index, word data) +{ + IO::out16(VBE_DISPI_IOPORT_INDEX, index); + IO::out16(VBE_DISPI_IOPORT_DATA, data); +} + +void BochsVGADevice::set_resolution(int width, int height) +{ + set_register(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED); + set_register(VBE_DISPI_INDEX_XRES, width); + set_register(VBE_DISPI_INDEX_YRES, height); + set_register(VBE_DISPI_INDEX_VIRT_WIDTH, width); + set_register(VBE_DISPI_INDEX_VIRT_HEIGHT, height); + set_register(VBE_DISPI_INDEX_BPP, 32); + set_register(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED); + set_register(VBE_DISPI_INDEX_BANK, 0); +} + +dword BochsVGADevice::find_framebuffer_address() +{ + static const PCI::ID bochs_vga_id = { 0x1234, 0x1111 }; + static const PCI::ID virtualbox_vga_id = { 0x80ee, 0xbeef }; + dword framebuffer_address = 0; + PCI::enumerate_all([&framebuffer_address] (const PCI::Address& address, PCI::ID id) { + if (id == bochs_vga_id || id == virtualbox_vga_id) { + framebuffer_address = PCI::get_BAR0(address) & 0xfffffff0; + kprintf("BochsVGA framebuffer @ PCI %w:%w BAR0=%x\n", id.vendor_id, id.device_id, framebuffer_address); + } + }); + return framebuffer_address; +} diff --git a/Kernel/BochsVGADevice.h b/Kernel/BochsVGADevice.h new file mode 100644 index 00000000000..eb4b9692df4 --- /dev/null +++ b/Kernel/BochsVGADevice.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include + +// FIXME: This should be a BlockDevice once we have BlockDevice. + +class BochsVGADevice { + AK_MAKE_ETERNAL +public: + static BochsVGADevice& the(); + static void initialize_statics(); + + BochsVGADevice(); + + PhysicalAddress framebuffer_address() const { return m_framebuffer_address; } + void set_resolution(int width, int height); + +private: + void set_register(word index, word value); + dword find_framebuffer_address(); + + PhysicalAddress m_framebuffer_address; +}; diff --git a/Kernel/Boot/boot.asm b/Kernel/Boot/boot.asm index 77caf40398e..fdd0027e94a 100755 --- a/Kernel/Boot/boot.asm +++ b/Kernel/Boot/boot.asm @@ -9,43 +9,6 @@ boot: mov ss, ax mov sp, 0xffff - ; get vesa modes - mov ax, 0x4f00 - xor dx, dx - mov es, dx - mov di, 0xc000 - mov [es:di], byte 'V' - mov [es:di+1], byte 'B' - mov [es:di+2], byte 'E' - mov [es:di+3], byte '2' - int 0x10 - cmp ax, 0x004f - jne fug - cmp [es:di], byte 'V' - jne fug - cmp [es:di+1], byte 'E' - jne fug - cmp [es:di+2], byte 'S' - jne fug - cmp [es:di+3], byte 'A' - jne fug - - ; get vesa info - mov ax, 0x4f01 - mov cx, 0x144 - xor dx, dx - mov es, dx - mov di, 0x2000 - int 0x10 - cmp ax, 0x004f - jne fug - - mov ax, 0x4f02 - mov bx, 0x4144 - int 0x10 - cmp ax, 0x004f - jne fug - push cs pop ds xor bx, bx diff --git a/Kernel/IO.h b/Kernel/IO.h index 2cdd9af6ecc..5761383375d 100644 --- a/Kernel/IO.h +++ b/Kernel/IO.h @@ -18,7 +18,7 @@ inline word in16(word port) return value; } -inline dword in32(dword port) +inline dword in32(word port) { dword value; asm volatile("inl %1, %0":"=a"(value):"Nd"(port)); diff --git a/Kernel/Makefile b/Kernel/Makefile index e547fa091da..8cd2888e353 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -30,6 +30,8 @@ KERNEL_OBJS = \ ELFLoader.o \ KSyms.o \ DevPtsFS.o \ + BochsVGADevice.o \ + PCI.o \ PS2MouseDevice.o \ GUIEventDevice.o diff --git a/Kernel/PCI.cpp b/Kernel/PCI.cpp new file mode 100644 index 00000000000..df82b9d6f57 --- /dev/null +++ b/Kernel/PCI.cpp @@ -0,0 +1,121 @@ +#include +#include + +#define PCI_VENDOR_ID 0x00 // word +#define PCI_DEVICE_ID 0x02 // word +#define PCI_COMMAND 0x04 // word +#define PCI_STATUS 0x06 // word +#define PCI_REVISION_ID 0x08 // byte +#define PCI_PROG_IF 0x09 // byte +#define PCI_SUBCLASS 0x0a // byte +#define PCI_CLASS 0x0b // byte +#define PCI_CACHE_LINE_SIZE 0x0c // byte +#define PCI_LATENCY_TIMER 0x0d // byte +#define PCI_HEADER_TYPE 0x0e // byte +#define PCI_BIST 0x0f // byte +#define PCI_BAR0 0x10 // dword +#define PCI_BAR1 0x14 // dword +#define PCI_BAR2 0x18 // dword +#define PCI_BAR3 0x1C // dword +#define PCI_BAR4 0x20 // dword +#define PCI_BAR5 0x24 // dword +#define PCI_INTERRUPT_LINE 0x3C // byte +#define PCI_SECONDARY_BUS 0x19 // byte +#define PCI_HEADER_TYPE_DEVICE 0 +#define PCI_HEADER_TYPE_BRIDGE 1 +#define PCI_TYPE_BRIDGE 0x0604 +#define PCI_ADDRESS_PORT 0xCF8 +#define PCI_VALUE_PORT 0xCFC +#define PCI_NONE 0xFFFF + +namespace PCI { + +template +T read_field(Address address, dword field) +{ + IO::out32(PCI_ADDRESS_PORT, address.io_address_for_field(field)); + if constexpr (sizeof(T) == 4) + return IO::in32(PCI_VALUE_PORT); + if constexpr (sizeof(T) == 2) + return IO::in16(PCI_VALUE_PORT + (field & 2)); + if constexpr (sizeof(T) == 1) + return IO::in8(PCI_VALUE_PORT + (field & 3)); +} + +template +void write_field(Address address, dword field, T value) +{ + IO::out32(PCI_ADDRESS_PORT, address.io_address_for_field(field)); + if constexpr (sizeof(T) == 4) + IO::out32(PCI_VALUE_PORT, value); + if constexpr (sizeof(T) == 2) + IO::out16(PCI_VALUE_PORT + (field & 2), value); + if constexpr (sizeof(T) == 1) + IO::out8(PCI_VALUE_PORT + (field & 3), value); +} + +word read_type(Address address) +{ + return (read_field(address, PCI_CLASS) << 8u) | read_field(address, PCI_SUBCLASS); +} + +void enumerate_bus(int type, byte bus, Function&); + +void enumerate_functions(int type, byte bus, byte slot, byte function, Function& callback) +{ + Address address(bus, slot, function); + if (type == -1 || type == read_type(address)) + callback(address, { read_field(address, PCI_VENDOR_ID), read_field(address, PCI_DEVICE_ID) }); + if (read_type(address) == PCI_TYPE_BRIDGE) { + byte secondary_bus = read_field(address, PCI_SECONDARY_BUS); + kprintf("PCI: Found secondary bus: %u\n", secondary_bus); + ASSERT(secondary_bus != bus); + enumerate_bus(type, secondary_bus, callback); + } +} + +void enumerate_slot(int type, byte bus, byte slot, Function& callback) +{ + Address address(bus, slot, 0); + if (read_field(address, PCI_VENDOR_ID) == PCI_NONE) + return; + enumerate_functions(type, bus, slot, 0, callback); + if (!(read_field(address, PCI_HEADER_TYPE) & 0x80)) + return; + for (byte function = 1; function < 8; ++function) { + Address address(bus, slot, function); + if (read_field(address, PCI_VENDOR_ID) != PCI_NONE) + enumerate_functions(type, bus, slot, function, callback); + } +} + +void enumerate_bus(int type, byte bus, Function& callback) +{ + for (byte slot = 0; slot < 32; ++slot) + enumerate_slot(type, bus, slot, callback); +} + +dword get_BAR0(Address address) { return read_field(address, PCI_BAR0); } +dword get_BAR1(Address address) { return read_field(address, PCI_BAR1); } +dword get_BAR2(Address address) { return read_field(address, PCI_BAR2); } +dword get_BAR3(Address address) { return read_field(address, PCI_BAR3); } +dword get_BAR4(Address address) { return read_field(address, PCI_BAR4); } +dword get_BAR5(Address address) { return read_field(address, PCI_BAR5); } + +void enumerate_all(Function callback) +{ + // Single PCI host controller. + if ((read_field(Address(), PCI_HEADER_TYPE) & 0x80) == 0) { + enumerate_bus(-1, 0, callback); + return; + } + + // Multiple PCI host controllers. + for (byte function = 0; function < 8; ++function) { + if (read_field(Address(0, 0, function), PCI_VENDOR_ID) == PCI_NONE) + break; + enumerate_bus(-1, function, callback); + } +} + +} diff --git a/Kernel/PCI.h b/Kernel/PCI.h new file mode 100644 index 00000000000..7ffb23172f3 --- /dev/null +++ b/Kernel/PCI.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include + +namespace PCI { + +struct ID { + word vendor_id { 0 }; + word device_id { 0 }; + + bool operator==(const ID& other) const + { + return vendor_id == other.vendor_id && device_id == other.device_id; + } +}; + +struct Address { + Address() { } + Address(byte bus, byte slot, byte function) : m_bus(bus), m_slot(slot), m_function(function) { } + + bool is_null() const { return !m_bus && !m_slot && !m_function; } + operator bool() const { return !is_null(); } + + byte bus() const { return m_bus; } + byte slot() const { return m_slot; } + byte function() const { return m_function; } + + dword io_address_for_field(byte field) const + { + return 0x80000000u | (m_bus << 16u) | (m_slot << 11u) | (m_function << 8u) | (field & 0xfc); + } + +private: + byte m_bus { 0 }; + byte m_slot { 0 }; + byte m_function { 0 }; +}; + +void enumerate_all(Function); +dword get_BAR0(Address); +dword get_BAR1(Address); +dword get_BAR2(Address); +dword get_BAR3(Address); +dword get_BAR4(Address); +dword get_BAR5(Address); + +} diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 40ae38c30ea..1e179a58fe9 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -1917,79 +1917,6 @@ clock_t Process::sys$times(tms* times) return 0; } -struct vbe_info_structure { - char signature[4]; // must be "VESA" to indicate valid VBE support - word version; // VBE version; high byte is major version, low byte is minor version - dword oem; // segment:offset pointer to OEM - dword capabilities; // bitfield that describes card capabilities - dword video_modes; // segment:offset pointer to list of supported video modes - word video_memory; // amount of video memory in 64KB blocks - word software_rev; // software revision - dword vendor; // segment:offset to card vendor string - dword product_name; // segment:offset to card model name - dword product_rev; // segment:offset pointer to product revision - char reserved[222]; // reserved for future expansion - char oem_data[256]; // OEM BIOSes store their strings in this area -} __attribute__ ((packed)); - -struct vbe_mode_info_structure { - word attributes; // deprecated, only bit 7 should be of interest to you, and it indicates the mode supports a linear frame buffer. - byte window_a; // deprecated - byte window_b; // deprecated - word granularity; // deprecated; used while calculating bank numbers - word window_size; - word segment_a; - word segment_b; - dword win_func_ptr; // deprecated; used to switch banks from protected mode without returning to real mode - word pitch; // number of bytes per horizontal line - word width; // width in pixels - word height; // height in pixels - byte w_char; // unused... - byte y_char; // ... - byte planes; - byte bpp; // bits per pixel in this mode - byte banks; // deprecated; total number of banks in this mode - byte memory_model; - byte bank_size; // deprecated; size of a bank, almost always 64 KB but may be 16 KB... - byte image_pages; - byte reserved0; - - byte red_mask; - byte red_position; - byte green_mask; - byte green_position; - byte blue_mask; - byte blue_position; - byte reserved_mask; - byte reserved_position; - byte direct_color_attributes; - - dword framebuffer; // physical address of the linear frame buffer; write here to draw to the screen - dword off_screen_mem_off; - word off_screen_mem_size; // size of memory in the framebuffer but not being displayed on the screen - byte reserved1[206]; -} __attribute__ ((packed)); - -DisplayInfo Process::get_display_info() -{ - DisplayInfo info; - //auto* vinfo = reinterpret_cast(0xc000); - auto* vmode = reinterpret_cast(0x2000); - dbgprintf("VESA framebuffer, %ux%u, %u bpp @ P%x\n", vmode->width, vmode->height, vmode->bpp, vmode->framebuffer); - dbgprintf("Returning display info in %s<%u>\n", name().characters(), pid()); - info.width = vmode->width; - info.height = vmode->height; - info.bpp = vmode->bpp; - info.pitch = vmode->pitch; - size_t framebuffer_size = info.pitch * info.height; - if (!m_display_framebuffer_region) { - auto framebuffer_vmo = VMObject::create_framebuffer_wrapper(PhysicalAddress(vmode->framebuffer), framebuffer_size); - m_display_framebuffer_region = allocate_region_with_vmo(LinearAddress(0xe0000000), framebuffer_size, move(framebuffer_vmo), 0, "framebuffer", true, true); - } - info.framebuffer = m_display_framebuffer_region->laddr().as_ptr(); - return info; -} - int Process::sys$select(const Syscall::SC_select_params* params) { if (!validate_read_typed(params)) diff --git a/Kernel/Process.h b/Kernel/Process.h index 6b6fe4f1b0f..51ba1744c27 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -217,7 +217,7 @@ public: int gui$set_window_rect(int window_id, const GUI_Rect*); int gui$set_global_cursor_tracking_enabled(int window_id, bool enabled); - DisplayInfo get_display_info(); + DisplayInfo set_video_resolution(int width, int height); static void initialize(); static void initialize_gui_statics(); diff --git a/Kernel/ProcessGUI.cpp b/Kernel/ProcessGUI.cpp index b1ec74795b0..8bdbd565d48 100644 --- a/Kernel/ProcessGUI.cpp +++ b/Kernel/ProcessGUI.cpp @@ -6,6 +6,7 @@ #include #include #include +#include //#define LOG_GUI_SYSCALLS @@ -271,3 +272,22 @@ void Process::destroy_all_windows() } m_windows.clear(); } + + +DisplayInfo Process::set_video_resolution(int width, int height) +{ + DisplayInfo info; + info.width = width; + info.height = height; + info.bpp = 32; + info.pitch = width * 4; + size_t framebuffer_size = width * height * 4; + if (!m_display_framebuffer_region) { + auto framebuffer_vmo = VMObject::create_framebuffer_wrapper(BochsVGADevice::the().framebuffer_address(), framebuffer_size); + m_display_framebuffer_region = allocate_region_with_vmo(LinearAddress(0xe0000000), framebuffer_size, move(framebuffer_vmo), 0, "framebuffer", true, true); + } + info.framebuffer = m_display_framebuffer_region->laddr().as_ptr(); + + BochsVGADevice::the().set_resolution(width, height); + return info; +} diff --git a/Kernel/init.cpp b/Kernel/init.cpp index 3fe0cfb9b18..655c1307bca 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -23,6 +23,7 @@ #include "PS2MouseDevice.h" #include "PTYMultiplexer.h" #include "DevPtsFS.h" +#include "BochsVGADevice.h" //#define SPAWN_GUITEST #define SPAWN_GUITEST2 @@ -170,11 +171,14 @@ void init() MemoryManager::initialize(); StringImpl::initialize_globals(); + BochsVGADevice::initialize_statics(); PIT::initialize(); memset(&system, 0, sizeof(system)); + new BochsVGADevice; + auto new_procfs = ProcFS::create(); new_procfs->initialize(); diff --git a/WindowServer/WSScreen.cpp b/WindowServer/WSScreen.cpp index 4c4fb88acae..c53ee7a6899 100644 --- a/WindowServer/WSScreen.cpp +++ b/WindowServer/WSScreen.cpp @@ -3,6 +3,7 @@ #include "WSMessage.h" #include "WSWindowManager.h" #include +#include static WSScreen* s_the; diff --git a/WindowServer/WSScreen.h b/WindowServer/WSScreen.h index 68705ed94c7..bc957b7b542 100644 --- a/WindowServer/WSScreen.h +++ b/WindowServer/WSScreen.h @@ -10,6 +10,8 @@ public: WSScreen(RGBA32*, unsigned width, unsigned height); ~WSScreen(); + void set_resolution(int width, int height); + int width() const { return m_width; } int height() const { return m_height; } RGBA32* scanline(int y); diff --git a/WindowServer/main.cpp b/WindowServer/main.cpp index 61da8ead624..23d10904452 100644 --- a/WindowServer/main.cpp +++ b/WindowServer/main.cpp @@ -10,7 +10,7 @@ void WindowServer_main() { - auto info = current->get_display_info(); + auto info = current->set_video_resolution(1024, 768); dbgprintf("Screen is %ux%ux%ubpp\n", info.width, info.height, info.bpp);