Serenity Operating System
at hosted 252 lines 8.8 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <Kernel/Devices/BXVGADevice.h> 28#include <Kernel/PCI/Access.h> 29#include <Kernel/Process.h> 30#include <Kernel/VM/AnonymousVMObject.h> 31#include <Kernel/VM/MemoryManager.h> 32#include <LibBareMetal/IO.h> 33#include <LibC/errno_numbers.h> 34#include <LibC/sys/ioctl_numbers.h> 35 36namespace Kernel { 37 38#define MAX_RESOLUTION_WIDTH 4096 39#define MAX_RESOLUTION_HEIGHT 2160 40 41#define VBE_DISPI_IOPORT_INDEX 0x01CE 42#define VBE_DISPI_IOPORT_DATA 0x01CF 43 44#define VBE_DISPI_INDEX_ID 0x0 45#define VBE_DISPI_INDEX_XRES 0x1 46#define VBE_DISPI_INDEX_YRES 0x2 47#define VBE_DISPI_INDEX_BPP 0x3 48#define VBE_DISPI_INDEX_ENABLE 0x4 49#define VBE_DISPI_INDEX_BANK 0x5 50#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 51#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 52#define VBE_DISPI_INDEX_X_OFFSET 0x8 53#define VBE_DISPI_INDEX_Y_OFFSET 0x9 54#define VBE_DISPI_DISABLED 0x00 55#define VBE_DISPI_ENABLED 0x01 56#define VBE_DISPI_LFB_ENABLED 0x40 57 58static BXVGADevice* s_the; 59 60BXVGADevice& BXVGADevice::the() 61{ 62 return *s_the; 63} 64 65BXVGADevice::BXVGADevice() 66 : BlockDevice(29, 0) 67 68{ 69 s_the = this; 70 m_framebuffer_address = PhysicalAddress(find_framebuffer_address()); 71 set_safe_resolution(); 72} 73 74void BXVGADevice::set_safe_resolution() 75{ 76 m_framebuffer_width = 1024; 77 m_framebuffer_height = 768; 78 m_framebuffer_pitch = m_framebuffer_width * sizeof(u32); 79 set_resolution(m_framebuffer_width, m_framebuffer_height); 80} 81 82void BXVGADevice::set_register(u16 index, u16 data) 83{ 84 IO::out16(VBE_DISPI_IOPORT_INDEX, index); 85 IO::out16(VBE_DISPI_IOPORT_DATA, data); 86} 87 88u16 BXVGADevice::get_register(u16 index) 89{ 90 IO::out16(VBE_DISPI_IOPORT_INDEX, index); 91 return IO::in16(VBE_DISPI_IOPORT_DATA); 92} 93 94void BXVGADevice::revert_resolution() 95{ 96 set_resolution_registers(m_framebuffer_width, m_framebuffer_height); 97 ASSERT(validate_setup_resolution(m_framebuffer_width, m_framebuffer_height)); 98} 99 100void BXVGADevice::set_resolution_registers(int width, int height) 101{ 102#ifdef BXVGA_DEBUG 103 dbg() << "BXVGADevice resolution registers set to - " << width << "x" << height; 104#endif 105 set_register(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED); 106 set_register(VBE_DISPI_INDEX_XRES, (u16)width); 107 set_register(VBE_DISPI_INDEX_YRES, (u16)height); 108 set_register(VBE_DISPI_INDEX_VIRT_WIDTH, (u16)width); 109 set_register(VBE_DISPI_INDEX_VIRT_HEIGHT, (u16)height * 2); 110 set_register(VBE_DISPI_INDEX_BPP, 32); 111 set_register(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED); 112 set_register(VBE_DISPI_INDEX_BANK, 0); 113} 114 115bool BXVGADevice::test_resolution(int width, int height) 116{ 117#ifdef BXVGA_DEBUG 118 dbg() << "BXVGADevice resolution test - " << width << "x" << height; 119#endif 120 set_resolution_registers(width, height); 121 bool resolution_changed = validate_setup_resolution(width, height); 122 revert_resolution(); 123 return resolution_changed; 124} 125bool BXVGADevice::set_resolution(int width, int height) 126{ 127 if (!test_resolution(width, height)) 128 return false; 129 130 set_resolution_registers(width, height); 131 dbg() << "BXVGADevice resolution set to " << width << "x" << height << " (pitch=" << m_framebuffer_pitch << ")"; 132 133 m_framebuffer_width = width; 134 m_framebuffer_height = height; 135 m_framebuffer_pitch = width * sizeof(u32); 136 return true; 137} 138 139bool BXVGADevice::validate_setup_resolution(int width, int height) 140{ 141 if ((u16)width != get_register(VBE_DISPI_INDEX_XRES) || (u16)height != get_register(VBE_DISPI_INDEX_YRES)) { 142 return false; 143 } 144 return true; 145} 146 147void BXVGADevice::set_y_offset(int y_offset) 148{ 149 ASSERT(y_offset == 0 || y_offset == m_framebuffer_height); 150 m_y_offset = y_offset; 151 set_register(VBE_DISPI_INDEX_Y_OFFSET, (u16)y_offset); 152} 153 154u32 BXVGADevice::find_framebuffer_address() 155{ 156 // NOTE: The QEMU card has the same PCI ID as the Bochs one. 157 static const PCI::ID bochs_vga_id = { 0x1234, 0x1111 }; 158 static const PCI::ID virtualbox_vga_id = { 0x80ee, 0xbeef }; 159 u32 framebuffer_address = 0; 160 PCI::enumerate_all([&framebuffer_address](const PCI::Address& address, PCI::ID id) { 161 if (id == bochs_vga_id || id == virtualbox_vga_id) { 162 framebuffer_address = PCI::get_BAR0(address) & 0xfffffff0; 163 klog() << "BXVGA: framebuffer @ " << PhysicalAddress(framebuffer_address); 164 } 165 }); 166 return framebuffer_address; 167} 168 169KResultOr<Region*> BXVGADevice::mmap(Process& process, FileDescription&, VirtualAddress preferred_vaddr, size_t offset, size_t size, int prot, bool shared) 170{ 171 REQUIRE_PROMISE(video); 172 if (!shared) 173 return KResult(-ENODEV); 174 ASSERT(offset == 0); 175 ASSERT(size == framebuffer_size_in_bytes()); 176 auto vmobject = AnonymousVMObject::create_for_physical_range(m_framebuffer_address, framebuffer_size_in_bytes()); 177 if (!vmobject) 178 return KResult(-ENOMEM); 179 auto* region = process.allocate_region_with_vmobject( 180 preferred_vaddr, 181 framebuffer_size_in_bytes(), 182 vmobject.release_nonnull(), 183 0, 184 "BXVGA Framebuffer", 185 prot); 186 dbg() << "BXVGADevice: mmap with size " << region->size() << " at " << region->vaddr(); 187 ASSERT(region); 188 return region; 189} 190 191int BXVGADevice::ioctl(FileDescription&, unsigned request, unsigned arg) 192{ 193 REQUIRE_PROMISE(video); 194 switch (request) { 195 case FB_IOCTL_GET_SIZE_IN_BYTES: { 196 auto* out = (size_t*)arg; 197 if (!Process::current->validate_write_typed(out)) 198 return -EFAULT; 199 *out = framebuffer_size_in_bytes(); 200 return 0; 201 } 202 case FB_IOCTL_GET_BUFFER: { 203 auto* index = (int*)arg; 204 if (!Process::current->validate_write_typed(index)) 205 return -EFAULT; 206 *index = m_y_offset == 0 ? 0 : 1; 207 return 0; 208 } 209 case FB_IOCTL_SET_BUFFER: { 210 if (arg != 0 && arg != 1) 211 return -EINVAL; 212 set_y_offset(arg == 0 ? 0 : m_framebuffer_height); 213 return 0; 214 } 215 case FB_IOCTL_GET_RESOLUTION: { 216 auto* resolution = (FBResolution*)arg; 217 if (!Process::current->validate_write_typed(resolution)) 218 return -EFAULT; 219 resolution->pitch = m_framebuffer_pitch; 220 resolution->width = m_framebuffer_width; 221 resolution->height = m_framebuffer_height; 222 return 0; 223 } 224 case FB_IOCTL_SET_RESOLUTION: { 225 auto* resolution = (FBResolution*)arg; 226 if (!Process::current->validate_read_typed(resolution) || !Process::current->validate_write_typed(resolution)) 227 return -EFAULT; 228 if (resolution->width > MAX_RESOLUTION_WIDTH || resolution->height > MAX_RESOLUTION_HEIGHT) 229 return -EINVAL; 230 if (!set_resolution(resolution->width, resolution->height)) { 231#ifdef BXVGA_DEBUG 232 dbg() << "Reverting Resolution: [" << m_framebuffer_width << "x" << m_framebuffer_height << "]"; 233#endif 234 resolution->pitch = m_framebuffer_pitch; 235 resolution->width = m_framebuffer_width; 236 resolution->height = m_framebuffer_height; 237 return -EINVAL; 238 } 239#ifdef BXVGA_DEBUG 240 dbg() << "New resolution: [" << m_framebuffer_width << "x" << m_framebuffer_height << "]"; 241#endif 242 resolution->pitch = m_framebuffer_pitch; 243 resolution->width = m_framebuffer_width; 244 resolution->height = m_framebuffer_height; 245 return 0; 246 } 247 default: 248 return -EINVAL; 249 }; 250} 251 252}