Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / video / i386 / pc / vbe.c
blob81e5a8e94618c99870166870d25895ee75bf94a4
1 /*
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
21 #include <grub/err.h>
22 #include <grub/machine/memory.h>
23 #include <grub/i386/pc/vbe.h>
24 #include <grub/video_fb.h>
25 #include <grub/types.h>
26 #include <grub/dl.h>
27 #include <grub/misc.h>
28 #include <grub/mm.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;
43 static struct
45 struct grub_video_mode_info mode_info;
47 grub_uint8_t *ptr;
48 int mtrr;
49 } framebuffer;
51 static grub_uint32_t initial_vbe_mode;
52 static grub_uint16_t *vbe_mode_list;
54 static void *
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) \
64 : "0" (num))
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. */
80 static void
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);
87 mask_lo = eax;
88 mask_hi = edx;
90 mask_lo |= 0x800 /* valid */;
91 wrmsr (mtrr_mask (mtrr), mask_lo, mask_hi);
94 static void
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;
100 int var_mtrrs;
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;
106 grub_uint64_t bits;
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
114 MTRR. */
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 */
123 size_bits = fb_size;
124 while (size_bits > 1)
125 size_bits >>= 1;
126 if (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 ())
137 return;
139 cpuid (1, eax, ebx, ecx, edx);
140 features = edx;
141 if (! (features & 0x00001000)) /* MTRR */
142 return;
144 rdmsr (0xFE, eax, edx);
145 mtrrcap = eax;
146 if (! (mtrrcap & 0x00000400)) /* write-combining */
147 return;
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);
157 else
158 maxphyaddr = 36;
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);
168 mask_lo = eax;
169 mask_hi = edx;
170 if (mask_lo & 0x800) /* valid */
172 grub_uint64_t real_base, real_mask;
174 rdmsr (mtrr_base (i), eax, edx);
175 base_lo = eax;
176 base_hi = edx;
178 real_base = ((grub_uint64_t) (base_hi & bits_hi) << 32) |
179 (base_lo & bits_lo);
180 real_mask = ((grub_uint64_t) (mask_hi & bits_hi) << 32) |
181 (mask_lo & bits_lo);
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)
187 first_unused = i;
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);
195 base_lo = eax;
196 base_hi = edx;
197 rdmsr (mtrr_mask (first_unused), eax, edx);
198 mask_lo = eax;
199 mask_hi = 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;
212 static void
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);
219 mask_lo = eax;
220 mask_hi = 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;
233 regs.eax = 0x4f09;
234 regs.ebx = 0;
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, &regs);
241 return regs.eax & 0xffff;
244 /* Call VESA BIOS 0x4f00 to get VBE Controller Information, return status. */
245 grub_vbe_status_t
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;
252 regs.eax = 0x4f00;
253 regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
254 grub_bios_interrupt (0x10, &regs);
255 return regs.eax & 0xffff;
258 /* Call VESA BIOS 0x4f01 to get VBE Mode Information, return status. */
259 grub_vbe_status_t
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;
264 regs.eax = 0x4f01;
265 regs.ecx = mode;
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, &regs);
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;
281 regs.eax = 0x4f02;
282 regs.ebx = mode;
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, &regs);
289 return regs.eax & 0xffff;
292 /* Call VESA BIOS 0x4f03 to return current VBE Mode, return status. */
293 grub_vbe_status_t
294 grub_vbe_bios_get_mode (grub_uint32_t *mode)
296 struct grub_bios_int_registers regs;
298 regs.eax = 0x4f03;
299 regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
300 grub_bios_interrupt (0x10, &regs);
301 *mode = regs.ebx & 0xffff;
303 return regs.eax & 0xffff;
306 grub_vbe_status_t
307 grub_vbe_bios_getset_dac_palette_width (int set, int *dac_mask_size)
309 struct grub_bios_int_registers regs;
311 regs.eax = 0x4f08;
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, &regs);
316 *dac_mask_size = (regs.ebx >> 8) & 0xff;
318 return regs.eax & 0xffff;
321 /* Call VESA BIOS 0x4f05 to set memory window, return status. */
322 grub_vbe_status_t
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;
330 regs.edx = position;
331 regs.eax = 0x4f05;
332 regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
333 grub_bios_interrupt (0x10, &regs);
334 return regs.eax & 0xffff;
337 /* Call VESA BIOS 0x4f05 to return memory window, return status. */
338 grub_vbe_status_t
339 grub_vbe_bios_get_memory_window (grub_uint32_t window,
340 grub_uint32_t *position)
342 struct grub_bios_int_registers regs;
344 regs.eax = 0x4f05;
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, &regs);
350 *position = regs.edx & 0xffff;
351 return regs.eax & 0xffff;
354 /* Call VESA BIOS 0x4f06 to set scanline length (in bytes), return status. */
355 grub_vbe_status_t
356 grub_vbe_bios_set_scanline_length (grub_uint32_t length)
358 struct grub_bios_int_registers regs;
360 regs.ecx = length;
361 regs.eax = 0x4f06;
362 /* BL = 2, Set Scan Line in Bytes. */
363 regs.ebx = 0x0002;
364 regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
365 grub_bios_interrupt (0x10, &regs);
366 return regs.eax & 0xffff;
369 /* Call VESA BIOS 0x4f06 to return scanline length (in bytes), return status. */
370 grub_vbe_status_t
371 grub_vbe_bios_get_scanline_length (grub_uint32_t *length)
373 struct grub_bios_int_registers regs;
375 regs.eax = 0x4f06;
376 regs.ebx = 0x0001;
377 regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
378 /* BL = 1, Get Scan Line Length (in bytes). */
379 grub_bios_interrupt (0x10, &regs);
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. */
395 regs.ecx = x;
396 regs.edx = y;
397 regs.eax = 0x4f07;
398 /* BL = 80h, Set Display Start during Vertical Retrace. */
399 regs.ebx = 0x0080;
400 regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
401 grub_bios_interrupt (0x10, &regs);
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. */
410 grub_vbe_status_t
411 grub_vbe_bios_get_display_start (grub_uint32_t *x,
412 grub_uint32_t *y)
414 struct grub_bios_int_registers regs;
416 regs.eax = 0x4f07;
417 /* BL = 1, Get Display Start. */
418 regs.ebx = 0x0001;
419 regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
420 grub_bios_interrupt (0x10, &regs);
422 *x = regs.ecx & 0xffff;
423 *y = regs.edx & 0xffff;
424 return regs.eax & 0xffff;
427 /* Call VESA BIOS 0x4f0a. */
428 grub_vbe_status_t
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;
434 regs.eax = 0x4f0a;
435 regs.ebx = 0x0000;
436 regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
437 grub_bios_interrupt (0x10, &regs);
439 if ((regs.eax & 0xffff) != GRUB_VBE_STATUS_OK)
441 *segment = 0;
442 *offset = 0;
443 *length = 0;
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;
458 regs.eax = 0x4f11;
459 regs.ebx = 0x0001;
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, &regs);
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;
473 regs.eax = 0x4f15;
474 regs.ebx = 0x0000;
475 regs.ecx = 0x0000;
476 regs.es = 0x0000;
477 regs.edi = 0x0000;
478 regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
479 grub_bios_interrupt (0x10, &regs);
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;
491 regs.eax = 0x4f15;
492 regs.ebx = 0x0001;
493 regs.ecx = 0x0000;
494 regs.edx = 0x0000;
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, &regs);
499 return regs.eax & 0xffff;
502 grub_err_t
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. */
509 if (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. */
519 vbe_detected = 0;
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. */
541 vbe_detected = 1;
545 if (! vbe_detected)
546 return grub_error (GRUB_ERR_BAD_DEVICE, "VESA BIOS Extension not found");
548 /* Make copy of controller info block to caller. */
549 if (info_block)
550 grub_memcpy (info_block, &controller_info, sizeof (*info_block));
552 return GRUB_ERR_NONE;
555 static grub_err_t
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. */
561 edid_info_lowmem =
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;
573 static grub_err_t
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");
613 static grub_err_t
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;
620 grub_err_t err;
622 /* Make sure that VBE is supported. */
623 grub_vbe_probe (0);
624 if (grub_errno != GRUB_ERR_NONE)
625 return grub_errno;
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)
630 return grub_errno;
632 /* For all VESA BIOS modes, force linear frame buffer. */
633 if (vbe_mode >= 0x100)
635 /* We only want linear frame buffer modes. */
636 vbe_mode |= 1 << 14;
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)
650 return grub_errno;
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;
663 else
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)
671 /* Text mode. */
673 /* No special action needed for text mode as it is not supported for
674 graphical support. */
676 else
678 /* Graphics mode. */
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;
686 unsigned i;
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,
698 0, palette);
700 /* Just ignore the status. */
701 err = grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS,
702 grub_video_fbstd_colors);
703 if (err)
704 return err;
709 /* Copy mode info for caller. */
710 if (vbe_mode_info)
711 grub_memcpy (vbe_mode_info, &new_vbe_mode_info, sizeof (*vbe_mode_info));
713 return GRUB_ERR_NONE;
716 grub_err_t
717 grub_vbe_get_video_mode (grub_uint32_t *mode)
719 grub_vbe_status_t status;
721 /* Make sure that VBE is supported. */
722 grub_vbe_probe (0);
723 if (grub_errno != GRUB_ERR_NONE)
724 return grub_errno;
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;
735 grub_err_t
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. */
744 grub_vbe_probe (0);
745 if (grub_errno != GRUB_ERR_NONE)
746 return grub_errno;
748 /* If mode is not VESA mode, skip mode info query. */
749 if (mode >= 0x100)
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));
760 else
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;
767 static grub_err_t
768 grub_video_vbe_init (void)
770 grub_uint16_t *rm_vbe_mode_list;
771 grub_uint16_t *p;
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)
783 return grub_errno;
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);
792 if (! vbe_mode_list)
793 return grub_errno;
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;
804 return grub_errno;
807 /* Reset frame buffer. */
808 grub_memset (&framebuffer, 0, sizeof(framebuffer));
809 framebuffer.mtrr = -1;
811 return grub_video_fb_init ();
814 static grub_err_t
815 grub_video_vbe_fini (void)
817 grub_vbe_status_t status;
818 grub_err_t err;
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. */
826 return grub_errno;
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;
840 return err;
844 Set framebuffer render target page and display the proper page, based on
845 `doublebuf_state.render_page' and `doublebuf_state.displayed_page',
846 respectively.
848 static grub_err_t
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");
861 return 0;
864 static void
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;
878 break;
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;
885 break;
887 case GRUB_VBE_MEMORY_MODEL_HERCULES:
888 mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_HERCULES
889 | GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP;
890 break;
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;
898 break;
900 case GRUB_VBE_MEMORY_MODEL_YUV:
901 mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_YUV;
902 break;
904 case GRUB_VBE_MEMORY_MODEL_DIRECT_COLOR:
905 mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_RGB;
906 break;
907 default:
908 mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_UNKNOWN;
909 break;
912 mode_info->bpp = vbeinfo->bits_per_pixel;
913 /* Calculate bytes_per_pixel value. */
914 switch(vbeinfo->bits_per_pixel)
916 case 32:
917 mode_info->bytes_per_pixel = 4;
918 break;
919 case 24:
920 mode_info->bytes_per_pixel = 3;
921 break;
922 case 16:
923 mode_info->bytes_per_pixel = 2;
924 break;
925 case 15:
926 mode_info->bytes_per_pixel = 2;
927 break;
928 case 8:
929 mode_info->bytes_per_pixel = 1;
930 break;
931 case 4:
932 mode_info->bytes_per_pixel = 0;
933 break;
936 if (controller_info.version >= 0x300)
937 mode_info->pitch = vbeinfo->lin_bytes_per_scan_line;
938 else
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);
954 static int
955 grub_video_vbe_iterate (int (*hook) (const struct grub_video_mode_info *info))
957 grub_uint16_t *p;
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;
968 break;
971 vbe2videoinfo (*p, &vbe_mode_info, &mode_info);
972 if (hook (&mode_info))
973 return 1;
975 return 0;
978 static grub_err_t
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)
983 grub_uint16_t *p;
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;
987 int depth;
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)
998 preferred_mode = 1;
999 else
1001 /* Fall back to 640x480. This is conservative, but the largest
1002 mode supported by the graphics card may not be safe for the
1003 display device. */
1004 grub_errno = GRUB_ERR_NONE;
1005 width = 640;
1006 height = 480;
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;
1020 break;
1023 if ((vbe_mode_info.mode_attributes & 0x001) == 0)
1024 /* If not available, skip it. */
1025 continue;
1027 if ((vbe_mode_info.mode_attributes & 0x008) == 0)
1028 /* Monochrome is unusable. */
1029 continue;
1031 if ((vbe_mode_info.mode_attributes & 0x080) == 0)
1032 /* We support only linear frame buffer modes. */
1033 continue;
1035 if ((vbe_mode_info.mode_attributes & 0x010) == 0)
1036 /* We allow only graphical modes. */
1037 continue;
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. */
1042 continue;
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 . */
1050 continue;
1052 if (preferred_mode)
1054 if (vbe_mode_info.x_resolution > width
1055 || vbe_mode_info.y_resolution > height)
1056 /* Resolution exceeds that of preferred mode. */
1057 continue;
1059 else
1061 if (((vbe_mode_info.x_resolution != width)
1062 || (vbe_mode_info.y_resolution != height))
1063 && width != 0 && height != 0)
1064 /* Non matching resolution. */
1065 continue;
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)))
1084 continue;
1087 /* If there is a request for specific depth, ignore others. */
1088 if ((depth != 0) && (vbe_mode_info.bits_per_pixel != depth))
1089 continue;
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)
1097 continue;
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)
1107 grub_err_t err;
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)
1113 return grub_errno;
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,
1129 framebuffer.ptr,
1130 doublebuf_pageflipping_set_page,
1131 framebuffer.ptr + page_size);
1132 else
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);
1145 return err;
1148 /* Couldn't found matching mode. */
1149 return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching mode found");
1152 static grub_err_t
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),
1161 // 0,
1162 // palette);
1166 /* Then set color to emulated palette. */
1168 return grub_video_fb_set_palette (start, count, palette_data);
1171 static grub_err_t
1172 grub_video_vbe_get_info_and_fini (struct grub_video_mode_info *mode_info,
1173 void **framebuf)
1175 grub_err_t err;
1176 grub_free (vbe_mode_list);
1177 vbe_mode_list = NULL;
1178 err = grub_video_fb_get_info_and_fini (mode_info, framebuf);
1179 if (err)
1180 return err;
1181 if (framebuffer.mtrr >= 0)
1183 grub_vbe_disable_mtrr (framebuffer.mtrr);
1184 framebuffer.mtrr = -1;
1186 return GRUB_ERR_NONE;
1189 static void
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,
1236 .next = 0
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);