2
* GRUB -- GRand Unified Bootloader
3
* Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc.
5
* GRUB is free software: you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation, either version 3 of the License, or
8
* (at your option) any later version.
10
* GRUB is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
19
#define grub_video_render_target grub_video_fbrender_target
22
#include <grub/machine/memory.h>
23
#include <grub/i386/pc/vbe.h>
24
#include <grub/video_fb.h>
25
#include <grub/types.h>
27
#include <grub/misc.h>
29
#include <grub/video.h>
30
#include <grub/i386/pc/int.h>
31
#include <grub/i18n.h>
32
#include <grub/cpu/tsc.h>
34
GRUB_MOD_LICENSE ("GPLv3+");
36
static int vbe_detected = -1;
38
static struct grub_vbe_info_block controller_info;
40
/* Track last mode to support cards which fail on get_mode. */
41
static grub_uint32_t last_set_mode = 3;
45
struct grub_video_mode_info mode_info;
51
static grub_uint32_t initial_vbe_mode;
52
static grub_uint16_t *vbe_mode_list;
55
real2pm (grub_vbe_farptr_t ptr)
57
return (void *) ((((unsigned long) ptr & 0xFFFF0000) >> 12UL)
58
+ ((unsigned long) ptr & 0x0000FFFF));
61
#define cpuid(num,a,b,c,d) \
62
asm volatile ("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1" \
63
: "=a" (a), "=r" (b), "=c" (c), "=d" (d) \
66
#define rdmsr(num,a,d) \
67
asm volatile ("rdmsr" : "=a" (a), "=d" (d) : "c" (num))
69
#define wrmsr(num,lo,hi) \
70
asm volatile ("wrmsr" : : "c" (num), "a" (lo), "d" (hi) : "memory")
72
#define mtrr_base(reg) (0x200 + (reg) * 2)
73
#define mtrr_mask(reg) (0x200 + (reg) * 2 + 1)
75
/* Try to set up a variable-range write-combining MTRR for a memory region.
76
This is best-effort; if it seems too hard, we just accept the performance
77
degradation rather than risking undefined behaviour. It is intended
78
exclusively to work around BIOS bugs, as the BIOS should already be
79
setting up a suitable MTRR. */
81
grub_vbe_enable_mtrr_entry (int mtrr)
83
grub_uint32_t eax, edx;
84
grub_uint32_t mask_lo, mask_hi;
86
rdmsr (mtrr_mask (mtrr), eax, edx);
90
mask_lo |= 0x800 /* valid */;
91
wrmsr (mtrr_mask (mtrr), mask_lo, mask_hi);
95
grub_vbe_enable_mtrr (grub_uint8_t *base, grub_size_t size)
97
grub_uint32_t eax, ebx, ecx, edx;
98
grub_uint32_t features;
99
grub_uint32_t mtrrcap;
101
grub_uint32_t max_extended_cpuid;
102
grub_uint32_t maxphyaddr;
103
grub_uint64_t fb_base, fb_size;
104
grub_uint64_t size_bits, fb_mask;
105
grub_uint32_t bits_lo, bits_hi;
107
int i, first_unused = -1;
108
grub_uint32_t base_lo, base_hi, mask_lo, mask_hi;
110
fb_base = (grub_uint64_t) (grub_size_t) base;
111
fb_size = (grub_uint64_t) size;
113
/* Check that fb_base and fb_size can be represented using a single
116
if (fb_base < (1 << 20))
117
return; /* under 1MB, so covered by fixed-range MTRRs */
118
if (fb_base >= (1LL << 36))
119
return; /* over 36 bits, so out of range */
120
if (fb_size < (1 << 12))
121
return; /* variable-range MTRRs must cover at least 4KB */
124
while (size_bits > 1)
127
return; /* not a power of two */
129
if (fb_base & (fb_size - 1))
130
return; /* not aligned on size boundary */
132
fb_mask = ~(fb_size - 1);
134
/* Check CPU capabilities. */
136
if (! grub_cpu_is_cpuid_supported ())
139
cpuid (1, eax, ebx, ecx, edx);
141
if (! (features & 0x00001000)) /* MTRR */
144
rdmsr (0xFE, eax, edx);
146
if (! (mtrrcap & 0x00000400)) /* write-combining */
148
var_mtrrs = (mtrrcap & 0xFF);
150
cpuid (0x80000000, eax, ebx, ecx, edx);
151
max_extended_cpuid = eax;
152
if (max_extended_cpuid >= 0x80000008)
154
cpuid (0x80000008, eax, ebx, ecx, edx);
155
maxphyaddr = (eax & 0xFF);
159
bits_lo = 0xFFFFF000; /* assume maxphyaddr >= 36 */
160
bits_hi = (1 << (maxphyaddr - 32)) - 1;
161
bits = bits_lo | ((grub_uint64_t) bits_hi << 32);
163
/* Check whether an MTRR already covers this region. If not, take an
164
unused one if possible. */
165
for (i = 0; i < var_mtrrs; i++)
167
rdmsr (mtrr_mask (i), eax, edx);
170
if (mask_lo & 0x800) /* valid */
172
grub_uint64_t real_base, real_mask;
174
rdmsr (mtrr_base (i), eax, edx);
178
real_base = ((grub_uint64_t) (base_hi & bits_hi) << 32) |
180
real_mask = ((grub_uint64_t) (mask_hi & bits_hi) << 32) |
182
if (real_base < (fb_base + fb_size) &&
183
real_base + (~real_mask & bits) >= fb_base)
184
return; /* existing MTRR overlaps this region */
186
else if (first_unused < 0)
190
if (first_unused < 0)
191
return; /* all MTRRs in use */
193
/* Set up the first unused MTRR we found. */
194
rdmsr (mtrr_base (first_unused), eax, edx);
197
rdmsr (mtrr_mask (first_unused), eax, edx);
201
base_lo = (base_lo & ~bits_lo & ~0xFF) |
202
(fb_base & bits_lo) | 0x01 /* WC */;
203
base_hi = (base_hi & ~bits_hi) | ((fb_base >> 32) & bits_hi);
204
wrmsr (mtrr_base (first_unused), base_lo, base_hi);
205
mask_lo = (mask_lo & ~bits_lo) | (fb_mask & bits_lo) | 0x800 /* valid */;
206
mask_hi = (mask_hi & ~bits_hi) | ((fb_mask >> 32) & bits_hi);
207
wrmsr (mtrr_mask (first_unused), mask_lo, mask_hi);
209
framebuffer.mtrr = first_unused;
213
grub_vbe_disable_mtrr (int mtrr)
215
grub_uint32_t eax, edx;
216
grub_uint32_t mask_lo, mask_hi;
218
rdmsr (mtrr_mask (mtrr), eax, edx);
222
mask_lo &= ~0x800 /* valid */;
223
wrmsr (mtrr_mask (mtrr), mask_lo, mask_hi);
226
/* Call VESA BIOS 0x4f09 to set palette data, return status. */
227
static grub_vbe_status_t
228
grub_vbe_bios_set_palette_data (grub_uint32_t color_count,
229
grub_uint32_t start_index,
230
struct grub_vbe_palette_data *palette_data)
232
struct grub_bios_int_registers regs;
235
regs.ecx = color_count;
236
regs.edx = start_index;
237
regs.es = (((grub_addr_t) palette_data) & 0xffff0000) >> 4;
238
regs.edi = ((grub_addr_t) palette_data) & 0xffff;
239
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
240
grub_bios_interrupt (0x10, ®s);
241
return regs.eax & 0xffff;
244
/* Call VESA BIOS 0x4f00 to get VBE Controller Information, return status. */
246
grub_vbe_bios_get_controller_info (struct grub_vbe_info_block *ci)
248
struct grub_bios_int_registers regs;
249
/* Store *controller_info to %es:%di. */
250
regs.es = (((grub_addr_t) ci) & 0xffff0000) >> 4;
251
regs.edi = ((grub_addr_t) ci) & 0xffff;
253
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
254
grub_bios_interrupt (0x10, ®s);
255
return regs.eax & 0xffff;
258
/* Call VESA BIOS 0x4f01 to get VBE Mode Information, return status. */
260
grub_vbe_bios_get_mode_info (grub_uint32_t mode,
261
struct grub_vbe_mode_info_block *mode_info)
263
struct grub_bios_int_registers regs;
266
/* Store *mode_info to %es:%di. */
267
regs.es = ((grub_addr_t) mode_info & 0xffff0000) >> 4;
268
regs.edi = (grub_addr_t) mode_info & 0x0000ffff;
269
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
270
grub_bios_interrupt (0x10, ®s);
271
return regs.eax & 0xffff;
274
/* Call VESA BIOS 0x4f02 to set video mode, return status. */
275
static grub_vbe_status_t
276
grub_vbe_bios_set_mode (grub_uint32_t mode,
277
struct grub_vbe_crtc_info_block *crtc_info)
279
struct grub_bios_int_registers regs;
283
/* Store *crtc_info to %es:%di. */
284
regs.es = (((grub_addr_t) crtc_info) & 0xffff0000) >> 4;
285
regs.edi = ((grub_addr_t) crtc_info) & 0xffff;
286
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
287
grub_bios_interrupt (0x10, ®s);
289
return regs.eax & 0xffff;
292
/* Call VESA BIOS 0x4f03 to return current VBE Mode, return status. */
294
grub_vbe_bios_get_mode (grub_uint32_t *mode)
296
struct grub_bios_int_registers regs;
299
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
300
grub_bios_interrupt (0x10, ®s);
301
*mode = regs.ebx & 0xffff;
303
return regs.eax & 0xffff;
307
grub_vbe_bios_getset_dac_palette_width (int set, int *dac_mask_size)
309
struct grub_bios_int_registers regs;
312
regs.ebx = (*dac_mask_size & 0xff) >> 8;
313
regs.ebx = set ? 1 : 0;
314
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
315
grub_bios_interrupt (0x10, ®s);
316
*dac_mask_size = (regs.ebx >> 8) & 0xff;
318
return regs.eax & 0xffff;
321
/* Call VESA BIOS 0x4f05 to set memory window, return status. */
323
grub_vbe_bios_set_memory_window (grub_uint32_t window,
324
grub_uint32_t position)
326
struct grub_bios_int_registers regs;
328
/* BL = window, BH = 0, Set memory window. */
329
regs.ebx = window & 0x00ff;
332
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
333
grub_bios_interrupt (0x10, ®s);
334
return regs.eax & 0xffff;
337
/* Call VESA BIOS 0x4f05 to return memory window, return status. */
339
grub_vbe_bios_get_memory_window (grub_uint32_t window,
340
grub_uint32_t *position)
342
struct grub_bios_int_registers regs;
345
/* BH = 1, Get memory window. BL = window. */
346
regs.ebx = (window & 0x00ff) | 0x100;
347
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
348
grub_bios_interrupt (0x10, ®s);
350
*position = regs.edx & 0xffff;
351
return regs.eax & 0xffff;
354
/* Call VESA BIOS 0x4f06 to set scanline length (in bytes), return status. */
356
grub_vbe_bios_set_scanline_length (grub_uint32_t length)
358
struct grub_bios_int_registers regs;
362
/* BL = 2, Set Scan Line in Bytes. */
364
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
365
grub_bios_interrupt (0x10, ®s);
366
return regs.eax & 0xffff;
369
/* Call VESA BIOS 0x4f06 to return scanline length (in bytes), return status. */
371
grub_vbe_bios_get_scanline_length (grub_uint32_t *length)
373
struct grub_bios_int_registers regs;
377
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
378
/* BL = 1, Get Scan Line Length (in bytes). */
379
grub_bios_interrupt (0x10, ®s);
381
*length = regs.ebx & 0xffff;
382
return regs.eax & 0xffff;
385
/* Call VESA BIOS 0x4f07 to set display start, return status. */
386
static grub_vbe_status_t
387
grub_vbe_bios_set_display_start (grub_uint32_t x, grub_uint32_t y)
389
struct grub_bios_int_registers regs;
391
if (framebuffer.mtrr >= 0)
392
grub_vbe_disable_mtrr (framebuffer.mtrr);
394
/* Store x in %ecx. */
398
/* BL = 80h, Set Display Start during Vertical Retrace. */
400
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
401
grub_bios_interrupt (0x10, ®s);
403
if (framebuffer.mtrr >= 0)
404
grub_vbe_enable_mtrr_entry (framebuffer.mtrr);
406
return regs.eax & 0xffff;
409
/* Call VESA BIOS 0x4f07 to get display start, return status. */
411
grub_vbe_bios_get_display_start (grub_uint32_t *x,
414
struct grub_bios_int_registers regs;
417
/* BL = 1, Get Display Start. */
419
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
420
grub_bios_interrupt (0x10, ®s);
422
*x = regs.ecx & 0xffff;
423
*y = regs.edx & 0xffff;
424
return regs.eax & 0xffff;
427
/* Call VESA BIOS 0x4f0a. */
429
grub_vbe_bios_get_pm_interface (grub_uint16_t *segment, grub_uint16_t *offset,
430
grub_uint16_t *length)
432
struct grub_bios_int_registers regs;
436
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
437
grub_bios_interrupt (0x10, ®s);
439
if ((regs.eax & 0xffff) != GRUB_VBE_STATUS_OK)
446
*segment = regs.es & 0xffff;
447
*offset = regs.edi & 0xffff;
448
*length = regs.ecx & 0xffff;
449
return regs.eax & 0xffff;
452
/* Call VESA BIOS 0x4f11 to get flat panel information, return status. */
453
static grub_vbe_status_t
454
grub_vbe_bios_get_flat_panel_info (struct grub_vbe_flat_panel_info *flat_panel_info)
456
struct grub_bios_int_registers regs;
460
regs.es = (((grub_addr_t) flat_panel_info) & 0xffff0000) >> 4;
461
regs.edi = ((grub_addr_t) flat_panel_info) & 0xffff;
462
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
463
grub_bios_interrupt (0x10, ®s);
464
return regs.eax & 0xffff;
467
/* Call VESA BIOS 0x4f15 to get DDC availability, return status. */
468
static grub_vbe_status_t
469
grub_vbe_bios_get_ddc_capabilities (grub_uint8_t *level)
471
struct grub_bios_int_registers regs;
478
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
479
grub_bios_interrupt (0x10, ®s);
481
*level = regs.ebx & 0xff;
482
return regs.eax & 0xffff;
485
/* Call VESA BIOS 0x4f15 to read EDID information, return status. */
486
static grub_vbe_status_t
487
grub_vbe_bios_read_edid (struct grub_video_edid_info *edid_info)
489
struct grub_bios_int_registers regs;
495
regs.es = (((grub_addr_t) edid_info) & 0xffff0000) >> 4;
496
regs.edi = ((grub_addr_t) edid_info) & 0xffff;
497
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
498
grub_bios_interrupt (0x10, ®s);
499
return regs.eax & 0xffff;
503
grub_vbe_probe (struct grub_vbe_info_block *info_block)
505
struct grub_vbe_info_block *vbe_ib;
506
grub_vbe_status_t status;
508
/* Clear caller's controller info block. */
510
grub_memset (info_block, 0, sizeof (*info_block));
512
/* Do not probe more than one time, if not necessary. */
513
if (vbe_detected == -1 || info_block)
515
/* Clear old copy of controller info block. */
516
grub_memset (&controller_info, 0, sizeof (controller_info));
518
/* Mark VESA BIOS extension as undetected. */
521
/* Use low memory scratch area as temporary storage
522
for VESA BIOS call. */
523
vbe_ib = (struct grub_vbe_info_block *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
525
/* Prepare info block. */
526
grub_memset (vbe_ib, 0, sizeof (*vbe_ib));
528
vbe_ib->signature[0] = 'V';
529
vbe_ib->signature[1] = 'B';
530
vbe_ib->signature[2] = 'E';
531
vbe_ib->signature[3] = '2';
533
/* Try to get controller info block. */
534
status = grub_vbe_bios_get_controller_info (vbe_ib);
535
if (status == GRUB_VBE_STATUS_OK)
537
/* Copy it for later usage. */
538
grub_memcpy (&controller_info, vbe_ib, sizeof (controller_info));
540
/* Mark VESA BIOS extension as detected. */
546
return grub_error (GRUB_ERR_BAD_DEVICE, "VESA BIOS Extension not found");
548
/* Make copy of controller info block to caller. */
550
grub_memcpy (info_block, &controller_info, sizeof (*info_block));
552
return GRUB_ERR_NONE;
556
grub_video_vbe_get_edid (struct grub_video_edid_info *edid_info)
558
struct grub_video_edid_info *edid_info_lowmem;
560
/* Use low memory scratch area as temporary storage for VESA BIOS calls. */
562
(struct grub_video_edid_info *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
563
grub_memset (edid_info_lowmem, 0, sizeof (*edid_info_lowmem));
565
if (grub_vbe_bios_read_edid (edid_info_lowmem) != GRUB_VBE_STATUS_OK)
566
return grub_error (GRUB_ERR_BAD_DEVICE, "EDID information not available");
568
grub_memcpy (edid_info, edid_info_lowmem, sizeof (*edid_info));
570
return GRUB_ERR_NONE;
574
grub_vbe_get_preferred_mode (unsigned int *width, unsigned int *height)
576
grub_vbe_status_t status;
577
grub_uint8_t ddc_level;
578
struct grub_video_edid_info edid_info;
579
struct grub_vbe_flat_panel_info *flat_panel_info;
581
/* Use low memory scratch area as temporary storage for VESA BIOS calls. */
582
flat_panel_info = (struct grub_vbe_flat_panel_info *)
583
(GRUB_MEMORY_MACHINE_SCRATCH_ADDR + sizeof (struct grub_video_edid_info));
585
if (controller_info.version >= 0x200
586
&& (grub_vbe_bios_get_ddc_capabilities (&ddc_level) & 0xff)
587
== GRUB_VBE_STATUS_OK)
589
if (grub_video_vbe_get_edid (&edid_info) == GRUB_ERR_NONE
590
&& grub_video_edid_checksum (&edid_info) == GRUB_ERR_NONE
591
&& grub_video_edid_preferred_mode (&edid_info, width, height)
592
== GRUB_ERR_NONE && *width < 4096 && *height < 4096)
593
return GRUB_ERR_NONE;
595
grub_errno = GRUB_ERR_NONE;
598
grub_memset (flat_panel_info, 0, sizeof (*flat_panel_info));
599
status = grub_vbe_bios_get_flat_panel_info (flat_panel_info);
600
if (status == GRUB_VBE_STATUS_OK
601
&& flat_panel_info->horizontal_size && flat_panel_info->vertical_size
602
&& flat_panel_info->horizontal_size < 4096
603
&& flat_panel_info->vertical_size < 4096)
605
*width = flat_panel_info->horizontal_size;
606
*height = flat_panel_info->vertical_size;
607
return GRUB_ERR_NONE;
610
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "cannot get preferred mode");
614
grub_vbe_set_video_mode (grub_uint32_t vbe_mode,
615
struct grub_vbe_mode_info_block *vbe_mode_info)
617
grub_vbe_status_t status;
618
grub_uint32_t old_vbe_mode;
619
struct grub_vbe_mode_info_block new_vbe_mode_info;
622
/* Make sure that VBE is supported. */
624
if (grub_errno != GRUB_ERR_NONE)
627
/* Try to get mode info. */
628
grub_vbe_get_video_mode_info (vbe_mode, &new_vbe_mode_info);
629
if (grub_errno != GRUB_ERR_NONE)
632
/* For all VESA BIOS modes, force linear frame buffer. */
633
if (vbe_mode >= 0x100)
635
/* We only want linear frame buffer modes. */
638
/* Determine frame buffer pixel format. */
639
if (new_vbe_mode_info.memory_model != GRUB_VBE_MEMORY_MODEL_PACKED_PIXEL
640
&& new_vbe_mode_info.memory_model
641
!= GRUB_VBE_MEMORY_MODEL_DIRECT_COLOR)
642
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
643
"unsupported pixel format 0x%x",
644
new_vbe_mode_info.memory_model);
647
/* Get current mode. */
648
grub_vbe_get_video_mode (&old_vbe_mode);
649
if (grub_errno != GRUB_ERR_NONE)
652
/* Try to set video mode. */
653
status = grub_vbe_bios_set_mode (vbe_mode, 0);
654
if (status != GRUB_VBE_STATUS_OK)
655
return grub_error (GRUB_ERR_BAD_DEVICE, "cannot set VBE mode %x", vbe_mode);
656
last_set_mode = vbe_mode;
658
if (vbe_mode < 0x100)
660
/* If this is not a VESA mode, guess address. */
661
framebuffer.ptr = (grub_uint8_t *) 0xa0000;
665
framebuffer.ptr = (grub_uint8_t *) new_vbe_mode_info.phys_base_addr;
668
/* Check whether mode is text mode or graphics mode. */
669
if (new_vbe_mode_info.memory_model == GRUB_VBE_MEMORY_MODEL_TEXT)
673
/* No special action needed for text mode as it is not supported for
674
graphical support. */
680
/* If video mode is in indexed color, setup default VGA palette. */
681
if (vbe_mode < 0x100 || new_vbe_mode_info.memory_model
682
== GRUB_VBE_MEMORY_MODEL_PACKED_PIXEL)
684
struct grub_vbe_palette_data *palette
685
= (struct grub_vbe_palette_data *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
688
/* Make sure that the BIOS can reach the palette. */
689
for (i = 0; i < GRUB_VIDEO_FBSTD_NUMCOLORS; i++)
691
palette[i].red = grub_video_fbstd_colors[i].r;
692
palette[i].green = grub_video_fbstd_colors[i].g;
693
palette[i].blue = grub_video_fbstd_colors[i].b;
694
palette[i].alignment = 0;
697
status = grub_vbe_bios_set_palette_data (GRUB_VIDEO_FBSTD_NUMCOLORS,
700
/* Just ignore the status. */
701
err = grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS,
702
grub_video_fbstd_colors);
709
/* Copy mode info for caller. */
711
grub_memcpy (vbe_mode_info, &new_vbe_mode_info, sizeof (*vbe_mode_info));
713
return GRUB_ERR_NONE;
717
grub_vbe_get_video_mode (grub_uint32_t *mode)
719
grub_vbe_status_t status;
721
/* Make sure that VBE is supported. */
723
if (grub_errno != GRUB_ERR_NONE)
726
/* Try to query current mode from VESA BIOS. */
727
status = grub_vbe_bios_get_mode (mode);
728
/* XXX: ATI cards don't support get_mode. */
729
if (status != GRUB_VBE_STATUS_OK)
730
*mode = last_set_mode;
732
return GRUB_ERR_NONE;
736
grub_vbe_get_video_mode_info (grub_uint32_t mode,
737
struct grub_vbe_mode_info_block *mode_info)
739
struct grub_vbe_mode_info_block *mi_tmp
740
= (struct grub_vbe_mode_info_block *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
741
grub_vbe_status_t status;
743
/* Make sure that VBE is supported. */
745
if (grub_errno != GRUB_ERR_NONE)
748
/* If mode is not VESA mode, skip mode info query. */
751
/* Try to get mode info from VESA BIOS. */
752
status = grub_vbe_bios_get_mode_info (mode, mi_tmp);
753
if (status != GRUB_VBE_STATUS_OK)
754
return grub_error (GRUB_ERR_BAD_DEVICE,
755
"cannot get information on the mode %x", mode);
757
/* Make copy of mode info block. */
758
grub_memcpy (mode_info, mi_tmp, sizeof (*mode_info));
761
/* Just clear mode info block if it isn't a VESA mode. */
762
grub_memset (mode_info, 0, sizeof (*mode_info));
764
return GRUB_ERR_NONE;
768
grub_video_vbe_init (void)
770
grub_uint16_t *rm_vbe_mode_list;
772
grub_size_t vbe_mode_list_size;
773
struct grub_vbe_info_block info_block;
775
/* Check if there is adapter present.
777
Firmware note: There has been a report that some cards store video mode
778
list in temporary memory. So we must first use vbe probe to get
779
refreshed information to receive valid pointers and data, and then
780
copy this information to somewhere safe. */
781
grub_vbe_probe (&info_block);
782
if (grub_errno != GRUB_ERR_NONE)
785
/* Copy modelist to local memory. */
786
p = rm_vbe_mode_list = real2pm (info_block.video_mode_ptr);
787
while(*p++ != 0xFFFF)
790
vbe_mode_list_size = (grub_addr_t) p - (grub_addr_t) rm_vbe_mode_list;
791
vbe_mode_list = grub_malloc (vbe_mode_list_size);
794
grub_memcpy (vbe_mode_list, rm_vbe_mode_list, vbe_mode_list_size);
796
/* Adapter could be found, figure out initial video mode. */
797
grub_vbe_get_video_mode (&initial_vbe_mode);
798
if (grub_errno != GRUB_ERR_NONE)
800
/* Free allocated resources. */
801
grub_free (vbe_mode_list);
802
vbe_mode_list = NULL;
807
/* Reset frame buffer. */
808
grub_memset (&framebuffer, 0, sizeof(framebuffer));
809
framebuffer.mtrr = -1;
811
return grub_video_fb_init ();
815
grub_video_vbe_fini (void)
817
grub_vbe_status_t status;
820
/* Restore old video mode. */
821
if (last_set_mode != initial_vbe_mode)
823
status = grub_vbe_bios_set_mode (initial_vbe_mode, 0);
824
if (status != GRUB_VBE_STATUS_OK)
825
/* TODO: Decide, is this something we want to do. */
828
last_set_mode = initial_vbe_mode;
830
/* TODO: Free any resources allocated by driver. */
831
grub_free (vbe_mode_list);
832
vbe_mode_list = NULL;
834
err = grub_video_fb_fini ();
835
if (framebuffer.mtrr >= 0)
837
grub_vbe_disable_mtrr (framebuffer.mtrr);
838
framebuffer.mtrr = -1;
844
Set framebuffer render target page and display the proper page, based on
845
`doublebuf_state.render_page' and `doublebuf_state.displayed_page',
849
doublebuf_pageflipping_set_page (int page)
851
/* Tell the video adapter to display the new front page. */
852
int display_start_line
853
= framebuffer.mode_info.height * page;
855
grub_vbe_status_t vbe_err =
856
grub_vbe_bios_set_display_start (0, display_start_line);
858
if (vbe_err != GRUB_VBE_STATUS_OK)
859
return grub_error (GRUB_ERR_IO, "couldn't commit pageflip");
865
vbe2videoinfo (grub_uint32_t mode,
866
const struct grub_vbe_mode_info_block *vbeinfo,
867
struct grub_video_mode_info *mode_info)
869
mode_info->mode_number = mode;
871
mode_info->width = vbeinfo->x_resolution;
872
mode_info->height = vbeinfo->y_resolution;
873
mode_info->mode_type = 0;
874
switch (vbeinfo->memory_model)
876
case GRUB_VBE_MEMORY_MODEL_TEXT:
877
mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_PURE_TEXT;
880
/* CGA is basically 4-bit packed pixel. */
881
case GRUB_VBE_MEMORY_MODEL_CGA:
882
mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_CGA;
883
case GRUB_VBE_MEMORY_MODEL_PACKED_PIXEL:
884
mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_INDEX_COLOR;
887
case GRUB_VBE_MEMORY_MODEL_HERCULES:
888
mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_HERCULES
889
| GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP;
892
/* Non chain 4 is a special case of planar. */
893
case GRUB_VBE_MEMORY_MODEL_NONCHAIN4_256:
894
mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_NONCHAIN4;
895
case GRUB_VBE_MEMORY_MODEL_PLANAR:
896
mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_PLANAR
897
| GRUB_VIDEO_MODE_TYPE_INDEX_COLOR;
900
case GRUB_VBE_MEMORY_MODEL_YUV:
901
mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_YUV;
904
case GRUB_VBE_MEMORY_MODEL_DIRECT_COLOR:
905
mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_RGB;
908
mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_UNKNOWN;
912
mode_info->bpp = vbeinfo->bits_per_pixel;
913
/* Calculate bytes_per_pixel value. */
914
switch(vbeinfo->bits_per_pixel)
917
mode_info->bytes_per_pixel = 4;
920
mode_info->bytes_per_pixel = 3;
923
mode_info->bytes_per_pixel = 2;
926
mode_info->bytes_per_pixel = 2;
929
mode_info->bytes_per_pixel = 1;
932
mode_info->bytes_per_pixel = 0;
936
if (controller_info.version >= 0x300)
937
mode_info->pitch = vbeinfo->lin_bytes_per_scan_line;
939
mode_info->pitch = vbeinfo->bytes_per_scan_line;
941
mode_info->number_of_colors = 256; /* TODO: fix me. */
942
mode_info->red_mask_size = vbeinfo->red_mask_size;
943
mode_info->red_field_pos = vbeinfo->red_field_position;
944
mode_info->green_mask_size = vbeinfo->green_mask_size;
945
mode_info->green_field_pos = vbeinfo->green_field_position;
946
mode_info->blue_mask_size = vbeinfo->blue_mask_size;
947
mode_info->blue_field_pos = vbeinfo->blue_field_position;
948
mode_info->reserved_mask_size = vbeinfo->rsvd_mask_size;
949
mode_info->reserved_field_pos = vbeinfo->rsvd_field_position;
951
mode_info->blit_format = grub_video_get_blit_format (mode_info);
955
grub_video_vbe_iterate (int (*hook) (const struct grub_video_mode_info *info))
958
struct grub_vbe_mode_info_block vbe_mode_info;
959
struct grub_video_mode_info mode_info;
961
for (p = vbe_mode_list; *p != 0xFFFF; p++)
963
grub_vbe_get_video_mode_info (*p, &vbe_mode_info);
964
if (grub_errno != GRUB_ERR_NONE)
966
/* Could not retrieve mode info, retreat. */
967
grub_errno = GRUB_ERR_NONE;
971
vbe2videoinfo (*p, &vbe_mode_info, &mode_info);
972
if (hook (&mode_info))
979
grub_video_vbe_setup (unsigned int width, unsigned int height,
980
grub_video_mode_type_t mode_type,
981
grub_video_mode_type_t mode_mask)
984
struct grub_vbe_mode_info_block vbe_mode_info;
985
struct grub_vbe_mode_info_block best_vbe_mode_info;
986
grub_uint32_t best_vbe_mode = 0;
988
int preferred_mode = 0;
990
/* Decode depth from mode_type. If it is zero, then autodetect. */
991
depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK)
992
>> GRUB_VIDEO_MODE_TYPE_DEPTH_POS;
994
if (width == 0 && height == 0)
996
grub_vbe_get_preferred_mode (&width, &height);
997
if (grub_errno == GRUB_ERR_NONE)
1001
/* Fall back to 640x480. This is conservative, but the largest
1002
mode supported by the graphics card may not be safe for the
1004
grub_errno = GRUB_ERR_NONE;
1010
/* Walk thru mode list and try to find matching mode. */
1011
for (p = vbe_mode_list; *p != 0xFFFF; p++)
1013
grub_uint32_t vbe_mode = *p;
1015
grub_vbe_get_video_mode_info (vbe_mode, &vbe_mode_info);
1016
if (grub_errno != GRUB_ERR_NONE)
1018
/* Could not retrieve mode info, retreat. */
1019
grub_errno = GRUB_ERR_NONE;
1023
if ((vbe_mode_info.mode_attributes & 0x001) == 0)
1024
/* If not available, skip it. */
1027
if ((vbe_mode_info.mode_attributes & 0x008) == 0)
1028
/* Monochrome is unusable. */
1031
if ((vbe_mode_info.mode_attributes & 0x080) == 0)
1032
/* We support only linear frame buffer modes. */
1035
if ((vbe_mode_info.mode_attributes & 0x010) == 0)
1036
/* We allow only graphical modes. */
1039
if ((vbe_mode_info.memory_model != GRUB_VBE_MEMORY_MODEL_PACKED_PIXEL)
1040
&& (vbe_mode_info.memory_model != GRUB_VBE_MEMORY_MODEL_DIRECT_COLOR))
1041
/* Not compatible memory model. */
1044
if (vbe_mode_info.bits_per_pixel != 8
1045
&& vbe_mode_info.bits_per_pixel != 15
1046
&& vbe_mode_info.bits_per_pixel != 16
1047
&& vbe_mode_info.bits_per_pixel != 24
1048
&& vbe_mode_info.bits_per_pixel != 32)
1049
/* Unsupported bitdepth . */
1054
if (vbe_mode_info.x_resolution > width
1055
|| vbe_mode_info.y_resolution > height)
1056
/* Resolution exceeds that of preferred mode. */
1061
if (((vbe_mode_info.x_resolution != width)
1062
|| (vbe_mode_info.y_resolution != height))
1063
&& width != 0 && height != 0)
1064
/* Non matching resolution. */
1068
/* Check if user requested RGB or index color mode. */
1069
if ((mode_mask & GRUB_VIDEO_MODE_TYPE_COLOR_MASK) != 0)
1071
unsigned my_mode_type = 0;
1073
if (vbe_mode_info.memory_model == GRUB_VBE_MEMORY_MODEL_PACKED_PIXEL)
1074
my_mode_type |= GRUB_VIDEO_MODE_TYPE_INDEX_COLOR;
1076
if (vbe_mode_info.memory_model == GRUB_VBE_MEMORY_MODEL_DIRECT_COLOR)
1077
my_mode_type |= GRUB_VIDEO_MODE_TYPE_RGB;
1079
if ((my_mode_type & mode_mask
1080
& (GRUB_VIDEO_MODE_TYPE_RGB | GRUB_VIDEO_MODE_TYPE_INDEX_COLOR))
1081
!= (mode_type & mode_mask
1082
& (GRUB_VIDEO_MODE_TYPE_RGB
1083
| GRUB_VIDEO_MODE_TYPE_INDEX_COLOR)))
1087
/* If there is a request for specific depth, ignore others. */
1088
if ((depth != 0) && (vbe_mode_info.bits_per_pixel != depth))
1091
/* Select mode with most of "volume" (size of framebuffer in bits). */
1092
if (best_vbe_mode != 0)
1093
if ((grub_uint64_t) vbe_mode_info.bits_per_pixel
1094
* vbe_mode_info.x_resolution * vbe_mode_info.y_resolution
1095
< (grub_uint64_t) best_vbe_mode_info.bits_per_pixel
1096
* best_vbe_mode_info.x_resolution * best_vbe_mode_info.y_resolution)
1099
/* Save so far best mode information for later use. */
1100
best_vbe_mode = vbe_mode;
1101
grub_memcpy (&best_vbe_mode_info, &vbe_mode_info, sizeof (vbe_mode_info));
1104
/* Try to initialize best mode found. */
1105
if (best_vbe_mode != 0)
1108
static struct grub_vbe_mode_info_block active_vbe_mode_info;
1109
/* If this fails, then we have mode selection heuristics problem,
1110
or adapter failure. */
1111
grub_vbe_set_video_mode (best_vbe_mode, &active_vbe_mode_info);
1112
if (grub_errno != GRUB_ERR_NONE)
1115
/* Fill mode info details. */
1116
vbe2videoinfo (best_vbe_mode, &active_vbe_mode_info,
1117
&framebuffer.mode_info);
1120
/* Get video RAM size in bytes. */
1121
grub_size_t vram_size = controller_info.total_memory << 16;
1122
grub_size_t page_size; /* The size of a page in bytes. */
1124
page_size = framebuffer.mode_info.pitch * framebuffer.mode_info.height;
1126
if (vram_size >= 2 * page_size)
1127
err = grub_video_fb_setup (mode_type, mode_mask,
1128
&framebuffer.mode_info,
1130
doublebuf_pageflipping_set_page,
1131
framebuffer.ptr + page_size);
1133
err = grub_video_fb_setup (mode_type, mode_mask,
1134
&framebuffer.mode_info,
1135
framebuffer.ptr, 0, 0);
1138
/* Copy default palette to initialize emulated palette. */
1139
err = grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS,
1140
grub_video_fbstd_colors);
1142
grub_vbe_enable_mtrr (framebuffer.ptr,
1143
controller_info.total_memory << 16);
1148
/* Couldn't found matching mode. */
1149
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching mode found");
1153
grub_video_vbe_set_palette (unsigned int start, unsigned int count,
1154
struct grub_video_palette_data *palette_data)
1156
if (framebuffer.mode_info.mode_type == GRUB_VIDEO_MODE_TYPE_INDEX_COLOR)
1158
/* TODO: Implement setting indexed color mode palette to hardware. */
1159
//status = grub_vbe_bios_set_palette_data (sizeof (vga_colors)
1160
// / sizeof (struct grub_vbe_palette_data),
1166
/* Then set color to emulated palette. */
1168
return grub_video_fb_set_palette (start, count, palette_data);
1172
grub_video_vbe_get_info_and_fini (struct grub_video_mode_info *mode_info,
1176
grub_free (vbe_mode_list);
1177
vbe_mode_list = NULL;
1178
err = grub_video_fb_get_info_and_fini (mode_info, framebuf);
1181
if (framebuffer.mtrr >= 0)
1183
grub_vbe_disable_mtrr (framebuffer.mtrr);
1184
framebuffer.mtrr = -1;
1186
return GRUB_ERR_NONE;
1190
grub_video_vbe_print_adapter_specific_info (void)
1192
grub_printf_ (N_(" VBE info: version: %d.%d OEM software rev: %d.%d\n"),
1193
controller_info.version >> 8,
1194
controller_info.version & 0xFF,
1195
controller_info.oem_software_rev >> 8,
1196
controller_info.oem_software_rev & 0xFF);
1198
/* The total_memory field is in 64 KiB units. */
1199
grub_printf_ (N_(" total memory: %d KiB\n"),
1200
(controller_info.total_memory << 16) / 1024);
1203
static struct grub_video_adapter grub_video_vbe_adapter =
1205
.name = "VESA BIOS Extension Video Driver",
1206
.id = GRUB_VIDEO_DRIVER_VBE,
1208
.prio = GRUB_VIDEO_ADAPTER_PRIO_FIRMWARE,
1210
.init = grub_video_vbe_init,
1211
.fini = grub_video_vbe_fini,
1212
.setup = grub_video_vbe_setup,
1213
.get_info = grub_video_fb_get_info,
1214
.get_info_and_fini = grub_video_vbe_get_info_and_fini,
1215
.set_palette = grub_video_vbe_set_palette,
1216
.get_palette = grub_video_fb_get_palette,
1217
.set_viewport = grub_video_fb_set_viewport,
1218
.get_viewport = grub_video_fb_get_viewport,
1219
.map_color = grub_video_fb_map_color,
1220
.map_rgb = grub_video_fb_map_rgb,
1221
.map_rgba = grub_video_fb_map_rgba,
1222
.unmap_color = grub_video_fb_unmap_color,
1223
.fill_rect = grub_video_fb_fill_rect,
1224
.blit_bitmap = grub_video_fb_blit_bitmap,
1225
.blit_render_target = grub_video_fb_blit_render_target,
1226
.scroll = grub_video_fb_scroll,
1227
.swap_buffers = grub_video_fb_swap_buffers,
1228
.create_render_target = grub_video_fb_create_render_target,
1229
.delete_render_target = grub_video_fb_delete_render_target,
1230
.set_active_render_target = grub_video_fb_set_active_render_target,
1231
.get_active_render_target = grub_video_fb_get_active_render_target,
1232
.iterate = grub_video_vbe_iterate,
1233
.get_edid = grub_video_vbe_get_edid,
1234
.print_adapter_specific_info = grub_video_vbe_print_adapter_specific_info,
1239
GRUB_MOD_INIT(video_i386_pc_vbe)
1241
grub_video_register (&grub_video_vbe_adapter);
1244
GRUB_MOD_FINI(video_i386_pc_vbe)
1246
grub_video_unregister (&grub_video_vbe_adapter);