Serenity Operating System
at portability 188 lines 6.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 s_the = this; 69 m_framebuffer_address = PhysicalAddress(find_framebuffer_address()); 70} 71 72void BXVGADevice::set_register(u16 index, u16 data) 73{ 74 IO::out16(VBE_DISPI_IOPORT_INDEX, index); 75 IO::out16(VBE_DISPI_IOPORT_DATA, data); 76} 77 78void BXVGADevice::set_resolution(int width, int height) 79{ 80 m_framebuffer_pitch = width * sizeof(u32); 81 m_framebuffer_width = width; 82 m_framebuffer_height = height; 83 84 set_register(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED); 85 set_register(VBE_DISPI_INDEX_XRES, (u16)width); 86 set_register(VBE_DISPI_INDEX_YRES, (u16)height); 87 set_register(VBE_DISPI_INDEX_VIRT_WIDTH, (u16)width); 88 set_register(VBE_DISPI_INDEX_VIRT_HEIGHT, (u16)height * 2); 89 set_register(VBE_DISPI_INDEX_BPP, 32); 90 set_register(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED); 91 set_register(VBE_DISPI_INDEX_BANK, 0); 92 93 dbg() << "BXVGADevice resolution set to " << m_framebuffer_width << "x" << m_framebuffer_height << " (pitch=" << m_framebuffer_pitch << ")"; 94} 95 96void BXVGADevice::set_y_offset(int y_offset) 97{ 98 ASSERT(y_offset == 0 || y_offset == m_framebuffer_height); 99 m_y_offset = y_offset; 100 set_register(VBE_DISPI_INDEX_Y_OFFSET, (u16)y_offset); 101} 102 103u32 BXVGADevice::find_framebuffer_address() 104{ 105 // NOTE: The QEMU card has the same PCI ID as the Bochs one. 106 static const PCI::ID bochs_vga_id = { 0x1234, 0x1111 }; 107 static const PCI::ID virtualbox_vga_id = { 0x80ee, 0xbeef }; 108 u32 framebuffer_address = 0; 109 PCI::enumerate_all([&framebuffer_address](const PCI::Address& address, PCI::ID id) { 110 if (id == bochs_vga_id || id == virtualbox_vga_id) { 111 framebuffer_address = PCI::get_BAR0(address) & 0xfffffff0; 112 kprintf("BXVGA: framebuffer @ P%x\n", framebuffer_address); 113 } 114 }); 115 return framebuffer_address; 116} 117 118KResultOr<Region*> BXVGADevice::mmap(Process& process, FileDescription&, VirtualAddress preferred_vaddr, size_t offset, size_t size, int prot) 119{ 120 REQUIRE_PROMISE(video); 121 ASSERT(offset == 0); 122 ASSERT(size == framebuffer_size_in_bytes()); 123 auto vmobject = AnonymousVMObject::create_for_physical_range(m_framebuffer_address, framebuffer_size_in_bytes()); 124 if (!vmobject) 125 return KResult(-ENOMEM); 126 auto* region = process.allocate_region_with_vmobject( 127 preferred_vaddr, 128 framebuffer_size_in_bytes(), 129 vmobject.release_nonnull(), 130 0, 131 "BXVGA Framebuffer", 132 prot); 133 dbg() << "BXVGADevice: mmap with size " << region->size() << " at " << region->vaddr(); 134 ASSERT(region); 135 return region; 136} 137 138int BXVGADevice::ioctl(FileDescription&, unsigned request, unsigned arg) 139{ 140 REQUIRE_PROMISE(video); 141 switch (request) { 142 case FB_IOCTL_GET_SIZE_IN_BYTES: { 143 auto* out = (size_t*)arg; 144 if (!Process::current->validate_write_typed(out)) 145 return -EFAULT; 146 *out = framebuffer_size_in_bytes(); 147 return 0; 148 } 149 case FB_IOCTL_GET_BUFFER: { 150 auto* index = (int*)arg; 151 if (!Process::current->validate_write_typed(index)) 152 return -EFAULT; 153 *index = m_y_offset == 0 ? 0 : 1; 154 return 0; 155 } 156 case FB_IOCTL_SET_BUFFER: { 157 if (arg != 0 && arg != 1) 158 return -EINVAL; 159 set_y_offset(arg == 0 ? 0 : m_framebuffer_height); 160 return 0; 161 } 162 case FB_IOCTL_GET_RESOLUTION: { 163 auto* resolution = (FBResolution*)arg; 164 if (!Process::current->validate_write_typed(resolution)) 165 return -EFAULT; 166 resolution->pitch = m_framebuffer_pitch; 167 resolution->width = m_framebuffer_width; 168 resolution->height = m_framebuffer_height; 169 return 0; 170 } 171 case FB_IOCTL_SET_RESOLUTION: { 172 auto* resolution = (FBResolution*)arg; 173 if (!Process::current->validate_read_typed(resolution) || !Process::current->validate_write_typed(resolution)) 174 return -EFAULT; 175 if (resolution->width > MAX_RESOLUTION_WIDTH || resolution->height > MAX_RESOLUTION_HEIGHT) 176 return -EINVAL; 177 set_resolution(resolution->width, resolution->height); 178 resolution->pitch = m_framebuffer_pitch; 179 resolution->width = m_framebuffer_width; 180 resolution->height = m_framebuffer_height; 181 return 0; 182 } 183 default: 184 return -EINVAL; 185 }; 186} 187 188}