:: limine / common / drivers / vbe.c 12.3 KB raw

1
#if defined (BIOS)
2
3
#include <stddef.h>
4
#include <stdint.h>
5
#include <stdbool.h>
6
#include <drivers/vbe.h>
7
#include <drivers/edid.h>
8
#include <lib/libc.h>
9
#include <lib/misc.h>
10
#include <lib/real.h>
11
#include <lib/print.h>
12
#include <lib/image.h>
13
#include <lib/config.h>
14
#include <lib/uri.h>
15
#include <lib/term.h>
16
#include <mm/pmm.h>
17
18
struct vbe_info_struct {
19
    char     signature[4];
20
    uint8_t  version_min;
21
    uint8_t  version_maj;
22
    uint16_t oem_off;
23
    uint16_t oem_seg;
24
    uint32_t capabilities;
25
    uint16_t vid_modes_off;
26
    uint16_t vid_modes_seg;
27
    uint16_t vid_mem_blocks;
28
    uint16_t software_rev;
29
    uint16_t vendor_off;
30
    uint16_t vendor_seg;
31
    uint16_t prod_name_off;
32
    uint16_t prod_name_seg;
33
    uint16_t prod_rev_off;
34
    uint16_t prod_rev_seg;
35
    uint8_t  reserved[222];
36
    uint8_t  oem_data[256];
37
} __attribute__((packed));
38
39
struct vbe_mode_info_struct {
40
    uint16_t mode_attributes;
41
    uint8_t  wina_attributes;
42
    uint8_t  winb_attributes;
43
    uint16_t win_granularity;
44
    uint16_t win_size;
45
    uint16_t wina_segment;
46
    uint16_t winb_segment;
47
    uint32_t win_farptr;
48
    uint16_t bytes_per_scanline;
49
50
    uint16_t res_x;
51
    uint16_t res_y;
52
    uint8_t  charsize_x;
53
    uint8_t  charsize_y;
54
    uint8_t  plane_count;
55
    uint8_t  bpp;
56
    uint8_t  bank_count;
57
    uint8_t  memory_model;
58
    uint8_t  bank_size;
59
    uint8_t  image_count;
60
    uint8_t  reserved0;
61
62
    uint8_t  red_mask_size;
63
    uint8_t  red_mask_shift;
64
    uint8_t  green_mask_size;
65
    uint8_t  green_mask_shift;
66
    uint8_t  blue_mask_size;
67
    uint8_t  blue_mask_shift;
68
    uint8_t  rsvd_mask_size;
69
    uint8_t  rsvd_mask_shift;
70
    uint8_t  direct_color_info;
71
72
    uint32_t framebuffer_addr;
73
    uint8_t  reserved1[6];
74
75
    uint16_t lin_bytes_per_scanline;
76
    uint8_t  banked_image_count;
77
    uint8_t  lin_image_count;
78
    uint8_t  lin_red_mask_size;
79
    uint8_t  lin_red_mask_shift;
80
    uint8_t  lin_green_mask_size;
81
    uint8_t  lin_green_mask_shift;
82
    uint8_t  lin_blue_mask_size;
83
    uint8_t  lin_blue_mask_shift;
84
    uint8_t  lin_rsvd_mask_size;
85
    uint8_t  lin_rsvd_mask_shift;
86
    uint32_t max_pixel_clock;
87
88
    uint8_t  reserved2[190];
89
} __attribute__((packed));
90
91
static bool get_vbe_info(struct vbe_info_struct *buf) {
92
    struct rm_regs r = {0};
93
94
    r.eax = 0x4f00;
95
    r.edi = (uint32_t)buf;
96
    rm_int(0x10, &r, &r);
97
98
    if ((r.eax & 0xff00) >> 8 != 0
99
     || (r.eax & 0x00ff) != 0x4f) {
100
        return false;
101
    }
102
103
    return true;
104
}
105
106
static bool get_vbe_mode_info(struct vbe_mode_info_struct *buf,
107
                              uint16_t mode) {
108
    struct rm_regs r = {0};
109
110
    r.eax = 0x4f01;
111
    r.ecx = (uint32_t)mode;
112
    r.edi = (uint32_t)buf;
113
    rm_int(0x10, &r, &r);
114
115
    if ((r.eax & 0xff00) >> 8 != 0
116
     || (r.eax & 0x00ff) != 0x4f) {
117
        return false;
118
    }
119
120
    return true;
121
}
122
123
static bool set_vbe_mode(uint16_t mode) {
124
    struct rm_regs r = {0};
125
126
    r.eax = 0x4f02;
127
    r.ebx = (uint32_t)mode | (1 << 14);
128
    rm_int(0x10, &r, &r);
129
130
    if ((r.eax & 0xff00) >> 8 != 0
131
     || (r.eax & 0x00ff) != 0x4f) {
132
        return false;
133
    }
134
135
    return true;
136
}
137
138
// Maximum number of video modes to enumerate to prevent infinite loops
139
// from corrupted VBE mode lists without a proper 0xffff terminator
140
#define VBE_MAX_MODES 512
141
142
struct fb_info *vbe_get_mode_list(size_t *count) {
143
    struct vbe_info_struct vbe_info;
144
    if (!get_vbe_info(&vbe_info)) {
145
        return NULL;
146
    }
147
148
    uint16_t *vid_modes = (uint16_t *)rm_desegment(vbe_info.vid_modes_seg,
149
                                                   vbe_info.vid_modes_off);
150
151
    size_t modes_count = 0;
152
    for (size_t i = 0; i < VBE_MAX_MODES && vid_modes[i] != 0xffff; i++) {
153
        struct vbe_mode_info_struct vbe_mode_info;
154
        if (!get_vbe_mode_info(&vbe_mode_info, vid_modes[i])) {
155
            continue;
156
        }
157
158
        // We only support RGB for now
159
        if (vbe_mode_info.memory_model != 0x06)
160
            continue;
161
        // We only support linear modes
162
        if (!(vbe_mode_info.mode_attributes & (1 << 7)))
163
            continue;
164
165
        uint16_t pitch = (vbe_info.version_maj < 3)
166
                       ? vbe_mode_info.bytes_per_scanline
167
                       : vbe_mode_info.lin_bytes_per_scanline;
168
        uint16_t bytes_per_pixel = vbe_mode_info.bpp / 8;
169
        if (bytes_per_pixel == 0
170
         || pitch % bytes_per_pixel != 0
171
         || pitch < (uint32_t)vbe_mode_info.res_x * bytes_per_pixel)
172
            continue;
173
174
        modes_count++;
175
    }
176
177
    struct fb_info *ret = ext_mem_alloc_counted(modes_count, sizeof(struct fb_info));
178
179
    for (size_t i = 0, j = 0; i < VBE_MAX_MODES && vid_modes[i] != 0xffff; i++) {
180
        struct vbe_mode_info_struct vbe_mode_info;
181
        if (!get_vbe_mode_info(&vbe_mode_info, vid_modes[i])) {
182
            continue;
183
        }
184
185
        // We only support RGB for now
186
        if (vbe_mode_info.memory_model != 0x06)
187
            continue;
188
        // We only support linear modes
189
        if (!(vbe_mode_info.mode_attributes & (1 << 7)))
190
            continue;
191
192
        uint16_t pitch = (vbe_info.version_maj < 3)
193
                       ? vbe_mode_info.bytes_per_scanline
194
                       : vbe_mode_info.lin_bytes_per_scanline;
195
        uint16_t bytes_per_pixel = vbe_mode_info.bpp / 8;
196
        if (bytes_per_pixel == 0
197
         || pitch % bytes_per_pixel != 0
198
         || pitch < (uint32_t)vbe_mode_info.res_x * bytes_per_pixel)
199
            continue;
200
201
        ret[j].memory_model = vbe_mode_info.memory_model;
202
203
        ret[j].framebuffer_width = vbe_mode_info.res_x;
204
        ret[j].framebuffer_height = vbe_mode_info.res_y;
205
        ret[j].framebuffer_bpp = vbe_mode_info.bpp;
206
207
        if (vbe_info.version_maj < 3) {
208
            ret[j].framebuffer_pitch  = vbe_mode_info.bytes_per_scanline;
209
            ret[j].red_mask_size      = vbe_mode_info.red_mask_size;
210
            ret[j].red_mask_shift     = vbe_mode_info.red_mask_shift;
211
            ret[j].green_mask_size    = vbe_mode_info.green_mask_size;
212
            ret[j].green_mask_shift   = vbe_mode_info.green_mask_shift;
213
            ret[j].blue_mask_size     = vbe_mode_info.blue_mask_size;
214
            ret[j].blue_mask_shift    = vbe_mode_info.blue_mask_shift;
215
        } else {
216
            ret[j].framebuffer_pitch  = vbe_mode_info.lin_bytes_per_scanline;
217
            ret[j].red_mask_size      = vbe_mode_info.lin_red_mask_size;
218
            ret[j].red_mask_shift     = vbe_mode_info.lin_red_mask_shift;
219
            ret[j].green_mask_size    = vbe_mode_info.lin_green_mask_size;
220
            ret[j].green_mask_shift   = vbe_mode_info.lin_green_mask_shift;
221
            ret[j].blue_mask_size     = vbe_mode_info.lin_blue_mask_size;
222
            ret[j].blue_mask_shift    = vbe_mode_info.lin_blue_mask_shift;
223
        }
224
225
        j++;
226
    }
227
228
    *count = modes_count;
229
230
    return ret;
231
}
232
233
bool init_vbe(struct fb_info *ret,
234
              uint16_t target_width, uint16_t target_height, uint16_t target_bpp) {
235
    printv("vbe: Initialising...\n");
236
237
    size_t current_fallback = 0;
238
239
    struct vbe_info_struct vbe_info;
240
    if (!get_vbe_info(&vbe_info)) {
241
        return false;
242
    }
243
244
    printv("vbe: Version: %u.%u\n", vbe_info.version_maj, vbe_info.version_min);
245
    printv("vbe: OEM: %s\n", (char *)rm_desegment(vbe_info.oem_seg, vbe_info.oem_off));
246
    printv("vbe: Graphics vendor: %s\n", (char *)rm_desegment(vbe_info.vendor_seg, vbe_info.vendor_off));
247
    printv("vbe: Product name: %s\n", (char *)rm_desegment(vbe_info.prod_name_seg, vbe_info.prod_name_off));
248
    printv("vbe: Product revision: %s\n", (char *)rm_desegment(vbe_info.prod_rev_seg, vbe_info.prod_rev_off));
249
250
    uint16_t *vid_modes = (uint16_t *)rm_desegment(vbe_info.vid_modes_seg,
251
                                                   vbe_info.vid_modes_off);
252
253
    struct resolution fallback_resolutions[] = {
254
        { 1024, 768, 32 },
255
        { 800,  600, 32 },
256
        { 640,  480, 32 },
257
        { 1024, 768, 24 },
258
        { 800,  600, 24 },
259
        { 640,  480, 24 },
260
        { 1024, 768, 16 },
261
        { 800,  600, 16 },
262
        { 640,  480, 16 }
263
    };
264
265
    if (!target_width || !target_height || !target_bpp) {
266
        struct edid_info_struct *edid_info = get_edid_info();
267
        if (edid_info != NULL) {
268
            int edid_width   = (int)edid_info->det_timing_desc1[2];
269
                edid_width  += ((int)edid_info->det_timing_desc1[4] & 0xf0) << 4;
270
            int edid_height  = (int)edid_info->det_timing_desc1[5];
271
                edid_height += ((int)edid_info->det_timing_desc1[7] & 0xf0) << 4;
272
            if (edid_width && edid_height) {
273
                target_width  = edid_width;
274
                target_height = edid_height;
275
                target_bpp    = 32;
276
                printv("vbe: EDID detected screen resolution of %ux%u\n",
277
                       target_width, target_height);
278
                goto retry;
279
            }
280
        }
281
        goto fallback;
282
    } else {
283
        printv("vbe: Requested resolution of %ux%ux%u\n",
284
               target_width, target_height, target_bpp);
285
    }
286
287
retry:
288
    for (size_t i = 0; i < VBE_MAX_MODES && vid_modes[i] != 0xffff; i++) {
289
        struct vbe_mode_info_struct vbe_mode_info;
290
        if (!get_vbe_mode_info(&vbe_mode_info, vid_modes[i])) {
291
            continue;
292
        }
293
        if  (vbe_mode_info.res_x == target_width
294
          && vbe_mode_info.res_y == target_height
295
          && vbe_mode_info.bpp   == target_bpp) {
296
            // We only support RGB for now
297
            if (vbe_mode_info.memory_model != 0x06)
298
                continue;
299
            // We only support linear modes
300
            if (!(vbe_mode_info.mode_attributes & (1 << 7)))
301
                continue;
302
            printv("vbe: Found matching mode %x, attempting to set...\n", vid_modes[i]);
303
            if (vid_modes[i] == current_video_mode) {
304
                printv("vbe: Mode was already set, perfect!\n");
305
            } else if (!set_vbe_mode(vid_modes[i])) {
306
                current_video_mode = -1;
307
                printv("vbe: Failed to set video mode %x, moving on...\n", vid_modes[i]);
308
                continue;
309
            }
310
            current_video_mode = vid_modes[i];
311
312
            printv("vbe: Framebuffer address: %x\n", vbe_mode_info.framebuffer_addr);
313
            ret->memory_model       = vbe_mode_info.memory_model;
314
            ret->framebuffer_addr   = vbe_mode_info.framebuffer_addr;
315
            ret->framebuffer_width  = vbe_mode_info.res_x;
316
            ret->framebuffer_height = vbe_mode_info.res_y;
317
            ret->framebuffer_bpp    = vbe_mode_info.bpp;
318
            if (vbe_info.version_maj < 3) {
319
                ret->framebuffer_pitch  = vbe_mode_info.bytes_per_scanline;
320
                ret->red_mask_size      = vbe_mode_info.red_mask_size;
321
                ret->red_mask_shift     = vbe_mode_info.red_mask_shift;
322
                ret->green_mask_size    = vbe_mode_info.green_mask_size;
323
                ret->green_mask_shift   = vbe_mode_info.green_mask_shift;
324
                ret->blue_mask_size     = vbe_mode_info.blue_mask_size;
325
                ret->blue_mask_shift    = vbe_mode_info.blue_mask_shift;
326
            } else {
327
                ret->framebuffer_pitch  = vbe_mode_info.lin_bytes_per_scanline;
328
                ret->red_mask_size      = vbe_mode_info.lin_red_mask_size;
329
                ret->red_mask_shift     = vbe_mode_info.lin_red_mask_shift;
330
                ret->green_mask_size    = vbe_mode_info.lin_green_mask_size;
331
                ret->green_mask_shift   = vbe_mode_info.lin_green_mask_shift;
332
                ret->blue_mask_size     = vbe_mode_info.lin_blue_mask_size;
333
                ret->blue_mask_shift    = vbe_mode_info.lin_blue_mask_shift;
334
            }
335
336
            uint16_t bytes_per_pixel = ret->framebuffer_bpp / 8;
337
            if (bytes_per_pixel == 0
338
             || ret->framebuffer_pitch % bytes_per_pixel != 0
339
             || ret->framebuffer_pitch < (uint32_t)ret->framebuffer_width * bytes_per_pixel) {
340
                printv("vbe: Mode %x has invalid pitch %u (width=%u, bpp=%u), skipping.\n",
341
                       vid_modes[i], (uint32_t)ret->framebuffer_pitch,
342
                       (uint32_t)ret->framebuffer_width, (uint32_t)ret->framebuffer_bpp);
343
                continue;
344
            }
345
346
            return true;
347
        }
348
    }
349
350
fallback:
351
    if (current_fallback < SIZEOF_ARRAY(fallback_resolutions)) {
352
        target_width  = fallback_resolutions[current_fallback].width;
353
        target_height = fallback_resolutions[current_fallback].height;
354
        target_bpp    = fallback_resolutions[current_fallback].bpp;
355
        current_fallback++;
356
        goto retry;
357
    }
358
359
    return false;
360
}
361
362
#endif
tab: 248 wrap: offon