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(&regs);
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(&regs);
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(&regs);
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(&regs);
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(&regs);
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(&regs);
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