1/** 2 * \file 3 * \brief VESA BIOS Extensions (VBE) 2.0 compatible video card driver. 4 * 5 * This driver only supports one primary video card that has already 6 * been booted by the system BIOS on native x86 hardware. Secondary 7 * cards are not supported. 8 */ 9 10/* 11 * Copyright (c) 2007, 2008, 2009, 2010, ETH Zurich. 12 * All rights reserved. 13 * 14 * This file is distributed under the terms in the attached LICENSE file. 15 * If you do not find this file, copies can be found by writing to: 16 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group. 17 */ 18 19#include <stdlib.h> 20#include <stdio.h> 21#include <string.h> 22#include <barrelfish/barrelfish.h> 23#include <acpi_client/acpi_client.h> 24#include <mackerel/mackerel.h> 25#include <inttypes.h> 26 27#include "vbe.h" 28#include "int10.h" 29 30/// Segment to put data into (needs to be free RAM) 31#define DATASEG 0x40000 32 33/// Maximum number of video modes supported 34#define MAX_MODES 256 35 36/// Pointer to emulated x86's RAM 37static void *myram = NULL; 38 39/// Array of all supported video modes for this adapter 40static uint16_t modes[MAX_MODES]; 41 42/// Array of BARs for the graphics card 43static struct device_mem *bars = NULL; 44 45/// Number of BARs in the array 46static int nbars = 0; 47 48/// The current video mode 49static uint16_t current_mode = 0; 50 51static inline uint32_t far_to_linear(uint32_t farptr) 52{ 53 uint32_t seg = farptr >> 16; 54 uint32_t off = farptr & 0xffff; 55 56 return (seg << 4) + off; 57} 58 59uint32_t vbe_controller_info(struct vbeinfoblock *ib) 60{ 61 struct int10_regs regs = { 62 .eax = 0x4f00, 63 .es = DATASEG >> 4, 64 .edi = 0 65 }; 66 67 struct vbeinfoblock *b = (struct vbeinfoblock *)(myram + DATASEG); 68 69 // Request extended VBE2.0 data 70 strncpy(b->signature, "VBE2", 4); 71 72 int10(®s); 73 *ib = *b; 74 75 return regs.eax & 0xffff; 76} 77 78uint32_t vbe_mode_info(uint16_t mode, struct vbemodeinfoblock *mib) 79{ 80 struct int10_regs regs = { 81 .eax = 0x4f01, 82 .ecx = mode, 83 .es = DATASEG >> 4, 84 .edi = 0 85 }; 86 87 int10(®s); 88 *mib = *(struct vbemodeinfoblock *)(myram + DATASEG); 89 90 return regs.eax & 0xffff; 91} 92 93uint32_t vbe_setmode(uint16_t mode, bool linear, bool clear) 94{ 95 struct int10_regs regs = { 96 .eax = 0x4f02, 97 .ebx = (mode & 0x1ff) | (linear ? 1 << 14 : 0) | (clear ? 0 : 1 << 15) 98 }; 99 100 int10(®s); 101 102 if((regs.eax & 0xffff) == VBE_OK) { 103 current_mode = mode; 104 } 105 106 return regs.eax & 0xffff; 107} 108 109uint32_t vbe_getmode(uint16_t *retmode, bool *retlinear) 110{ 111 struct int10_regs regs = { 112 .eax = 0x4f03, 113 }; 114 115 int10(®s); 116 117 if((regs.eax & 0xffff) == VBE_OK) { 118 *retmode = regs.ebx & 0x3fff; 119 *retlinear = (regs.ebx & (1u << 14)) != 0; 120 } 121 122 return regs.eax & 0xffff; 123} 124 125uint16_t *vbe_getmodes(void) 126{ 127 return modes; 128} 129 130// FIXME: in the long run we probably don't want to be 131// handing out the raw hardware cap anyway... 132errval_t vbe_get_framebuffer_cap(struct capref *cap, size_t *retoffset) 133{ 134 struct vbemodeinfoblock mib; 135 int first_mem_bar = -1; 136 137 uint32_t r = vbe_mode_info(current_mode, &mib); 138 assert((r & 0xffff) == VBE_OK); 139 140 lpaddr_t fbphys = mib.physbaseptr; 141 size_t fbsize = (size_t)mib.xresolution * mib.yresolution 142 * (mib.bitsperpixel / 8); 143 144 printf("vbe: looking for BAR cap with PA %"PRIuLPADDR"-%"PRIuLPADDR"\n", 145 fbphys, fbphys + fbsize); 146 147 for(int i = 0; i < nbars; i++) { 148 if (bars[i].type == 0) { // memory bar 149 printf("%d: %"PRIxGENPADDR"-%"PRIxGENPADDR"\n", i, 150 bars[i].paddr, bars[i].paddr + bars[i].bytes); 151 if (first_mem_bar == -1) { 152 first_mem_bar = i; 153 } 154 if (bars[i].paddr <= fbphys 155 && bars[i].paddr + bars[i].bytes >= fbphys + fbsize) { 156 // it's in this BAR, but do we have a single cap that covers it? 157 // XXX: assume uniformly-sized caps 158 size_t fboffset = fbphys - bars[i].paddr; 159 160 // does framebuf start at cap boundary? 161 *cap = bars[i].frame_cap; 162 *retoffset = fboffset; 163 return SYS_ERR_OK; 164 } 165 } else { 166 printf("%d: IO cap\n", i); 167 } 168 } 169 170 // XXX: Hack to get right framebuffer 171 printf("vbe: not found, falling back to %d: %" PRIxGENPADDR "\n", 172 first_mem_bar, bars[first_mem_bar].paddr); 173 *cap = bars[first_mem_bar].frame_cap; 174 175 return SYS_ERR_OK; 176} 177 178/* XXX: hack to wait for a vertical retrace */ 179void vbe_vsync(void) 180{ 181 /* wait until any previous retrace has ended */ 182 while(mackerel_read_io_8(0, 0x3da) & 8); 183 184 /* wait until a new retrace has just begun */ 185 while(!(mackerel_read_io_8(0, 0x3da) & 8)); 186} 187 188static char *buf = NULL; 189static size_t bufsize = 0; 190 191uint32_t vbe_savestate(void) 192{ 193 // Request size of state buffer 194 struct int10_regs regs = { 195 .eax = 0x4f04, 196 .edx = 0, 197 .ecx = 0xf, 198 }; 199 int10(®s); 200 bufsize = regs.ebx * 64; 201 buf = realloc(buf, bufsize); 202 assert(buf != NULL); 203 204 // Save buffer 205 struct int10_regs nregs = { 206 .eax = 0x4f04, 207 .edx = 1, 208 .ecx = 0xf, 209 .es = DATASEG >> 4, 210 .ebx = 0 211 }; 212 int10(&nregs); 213 214 char *b = (myram + DATASEG); 215 memcpy(buf, b, bufsize); 216 217 return nregs.eax & 0xffff; 218} 219 220uint32_t vbe_restorestate(void) 221{ 222 assert(buf != NULL); 223 char *b = (myram + DATASEG); 224 memcpy(b, buf, bufsize); 225 226 // Restore buffer 227 struct int10_regs regs = { 228 .eax = 0x4f04, 229 .edx = 2, 230 .ecx = 0xf, 231 .es = DATASEG >> 4, 232 .ebx = 0 233 }; 234 int10(®s); 235 236 return regs.eax & 0xffff; 237} 238 239void vbe_init(void *arg, struct device_mem *bar_info, int nr_mapped_regions) 240{ 241 errval_t err; 242 printf("vbe: initialising graphics hardware...\n"); 243 244 // Map BIOS memory region 245 struct capref bioscap; 246 size_t size; 247 err = acpi_get_vbe_bios_cap(&bioscap, &size); 248 if (err_is_fail(err)) { 249 DEBUG_ERR(err, "pci_get_vbe_bios_cap failed"); 250 return; 251 } 252 253 void *lowmem; 254 err = vspace_map_one_frame(&lowmem, size, bioscap, NULL, NULL); 255 if (err_is_fail(err)) { 256 DEBUG_ERR(err, "vspace_map_one_frame failed"); 257 return; 258 } 259 260 myram = malloc(REALMODE_MEM_SIZE); 261 memcpy(myram, lowmem, REALMODE_MEM_SIZE); 262 263 int10_init(myram); 264 265 struct vbeinfoblock ib; 266 267 uint32_t r = vbe_controller_info(&ib); 268 assert(r == VBE_OK); 269 270 if(strncmp(ib.signature, "VESA", 4) || ib.version < 0x200) { 271 printf("Graphics card does not support at least VBE 2.0\n"); 272 return; 273 } else { 274 printf("VBE %u.%u compatible graphics card: %s\n", 275 ib.version >> 8, ib.version & 0xff, 276 (char *)myram + far_to_linear(ib.oemstringptr)); 277 printf("Available video memory: %u MB\n", (ib.totalmemory << 16) / 1024 / 1024); 278 279 // make a local copy of video modes (256 modes should be enough) 280 memcpy(modes, myram + far_to_linear(ib.videomodeptr), 256 * 2); 281 282#if 0 283 // Print supported VESA modes 284 for(int i = 0; modes[i] != 0xffff; i++) { 285 struct vbemodeinfoblock mib; 286 287 if(vbe_mode_info(modes[i], &mib) != VBE_OK) { 288 return; 289 } 290 291 if (mib.xresolution == 0 && mib.yresolution == 0) { 292 continue; 293 } 294 295 printf("Mode 0x%x (%dx%dx%d) [0x%x:0x%x:0x%x]: %s%s%s%s%s%s%s\n", 296 modes[i], mib.xresolution, mib.yresolution, mib.bitsperpixel, 297 mib.winasegment, mib.winbsegment, mib.physbaseptr, 298 mib.modeattributes & MODE_SUPPORTED ? "supported" : "", 299 mib.modeattributes & TTY_OUTPUT_SUPPORTED ? " TTY" : "", 300 mib.modeattributes & COLOR_MODE ? " color" : " monochrome", 301 mib.modeattributes & GRAPHICS_MODE ? " graphics" : " text", 302 mib.modeattributes & MODE_NOT_VGA_COMPATIBLE ? "" : " VGA", 303 mib.modeattributes & WINDOWED_MODE_NOT_AVAILABLE ? "" : " windowed", 304 mib.modeattributes & LINEAR_MODE_AVAILABLE ? " linear" : ""); 305 } 306#endif 307 } 308 309 uint16_t curmode = 0; 310 bool islinear = false; 311 r = vbe_getmode(&curmode, &islinear); 312 assert(r == VBE_OK); 313 printf("Current mode: 0x%x %s\n", curmode, islinear ? "linear": "windowed"); 314 315 bars = bar_info; 316 nbars = nr_mapped_regions; 317 318 vbe_driver_init_done(); 319} 320