Hello,
here is the VBE patch against the current CVS. I did my best to write a
conforming ChangeLog entry. Let me know if there is something wrong - it's
the first time that I contribute to an open source project.
I will send a patch that fills in the remaining fields in the multiboot info
header later.
What do you think the format of this 'videomode' builtin should be? I wanted
to pass only the VBE mode number, but it seems that this is not general
enough.
I am also thinking about handling graphics requests where some fields are set
to zero: choosing an appropriate mode would require quite an amount of code
and I would not like to squeeze all of it into load_image which is quite hard
to read already. Don't you think this function needs some clean-up to remove
all these nested if/else constructs? If you want to add something new it's
quite hard to see where it should go.
Regards,
Alex
On Tuesday 13 November 2001 17:45, you wrote:
> At Tue, 13 Nov 2001 12:37:29 +0100,
>
> Alexander Thiel wrote:
> > as the latest version of GRUB did not support VBE graphics as decribed in
> > the multiboot specification, but I wanted it for my own toy kernel, I
> > have added it myself. I could not find any hint about ongoing work for
> > VBE support in the mailing list archive, so I decided to post my patch
> > (against grub-0.90). It moves some existing code around and adds some new
> > chunks. Here is a summary of the changes:
>
> Thanks for your contribution very much! Could you write ChangeLog
> entries, as descibed in the GNU Coding Standards?
>
> > As of yet, there is only the default text mode supported, and the info on
> > the protected mode interface is not available. I am also thinking about
> > adding a builtin function 'setvbe' to explicitly set the mode passed to
> > the kernel.
>
> Agreed. I think "videomode" would be more appropriate, since we may
> add support for other modes than VBE later.
>
> Okuji
Index: ChangeLog
===================================================================
RCS file: /cvsroot/grub/grub/ChangeLog,v
retrieving revision 1.441
diff -u -r1.441 ChangeLog
--- ChangeLog 2001/11/12 06:57:28 1.441
+++ ChangeLog 2001/11/21 16:21:59
@@ -1,3 +1,27 @@
+2001-11-18 Alexander Thiel <[EMAIL PROTECTED]>
+
+ * stage2/shared.h (VBE_FAR_PTR): New macro.
+ (mbh): Declared.
+ (vbe_info_block): Likewise.
+ (mode_info_block): Likewise.
+ (mode_list): Likewise.
+ * stage2/common.c: (vbe_info_block): Defined.
+ (mode_info_block): Likewise.
+ (mode_list): Likewise.
+ (init_bios_info): Probe for VBE compliant BIOS and
+ set mbi.flags accordingly.
+ * stage2/boot.c (mbh): Defined.
+ (load_image): Replaced pu.mb by mbh and flags by mbh->flags.
+ Added code to handle graphics request from multiboot kernels.
+ * stage2/builtins.c:
+ (boot_func): Switch video mode before booting
+ a multiboot kernel.
+ (testvbe_func): Renamed controller to vbe_info_block and mode to
+ mode_info_block. Check flag MB_INFO_VIDEO_INFO instead of probing
+ the BIOS.
+ (vbeprobe): Likewise. Replaced vbe_far_ptr_to_linear by VBE_FAR_PTR.
+ Use global mode_list.
+
2001-11-12 Yoshinori K. Okuji <[EMAIL PROTECTED]>
* docs/grub.texi: The copyright of this file is only held by
Index: stage2/boot.c
===================================================================
RCS file: /cvsroot/grub/grub/stage2/boot.c,v
retrieving revision 1.36
diff -u -r1.36 boot.c
--- stage2/boot.c 2001/11/12 06:57:29 1.36
+++ stage2/boot.c 2001/11/21 16:21:59
@@ -29,6 +29,7 @@
entry_func entry_addr;
static struct mod_list mll[99];
+struct multiboot_header *mbh;
/*
* The next two functions, 'load_image' and 'load_module', are the building
@@ -43,12 +44,11 @@
int len, i, exec_type = 0, align_4k = 1;
entry_func real_entry_addr = 0;
kernel_t type = KERNEL_TYPE_NONE;
- unsigned long flags = 0, text_len = 0, data_len = 0, bss_len = 0;
+ unsigned long text_len = 0, data_len = 0, bss_len = 0;
char *str = 0, *str2 = 0;
struct linux_kernel_header *lh;
union
{
- struct multiboot_header *mb;
struct exec *aout;
Elf32_Ehdr *elf;
}
@@ -57,10 +57,6 @@
executable header */
unsigned char buffer[MULTIBOOT_SEARCH];
- /* sets the header pointer to point to the beginning of the
- buffer by default */
- pu.aout = (struct exec *) buffer;
-
if (!grub_open (kernel))
return KERNEL_TYPE_NONE;
@@ -78,8 +74,8 @@
{
if (MULTIBOOT_FOUND ((int) (buffer + i), len - i))
{
- flags = ((struct multiboot_header *) (buffer + i))->flags;
- if (flags & MULTIBOOT_UNSUPPORTED)
+ mbh = (struct multiboot_header *) (buffer + i);
+ if (mbh->flags & MULTIBOOT_UNSUPPORTED)
{
grub_close ();
errnum = ERR_BOOT_FEATURES;
@@ -91,6 +87,57 @@
}
}
+ /* Handle graphics request for multiboot kernels */
+ if (type == KERNEL_TYPE_MULTIBOOT &&
+ mbh->flags & MULTIBOOT_VIDEO_MODE &&
+ mbi.flags & MB_INFO_VIDEO_INFO)
+ {
+ mbi.vbe_mode = 0x03; /* default */
+
+ if (mbh->mode_type == 0)
+ {
+ unsigned short fallback = 0xFFFF;
+
+ for (mode_list
+ = (unsigned short *) VBE_FAR_PTR (vbe_info_block.video_mode);
+ *mode_list != 0xFFFF;
+ mode_list++)
+ {
+ if (get_vbe_mode_info (*mode_list, &mode_info_block) != 0x004F
+ || (mode_info_block.mode_attributes & 0x0091) != 0x0091)
+ continue;
+
+ /* use the first valid mode as fallback */
+ if (fallback == 0xFFFF) fallback = *mode_list;
+
+ if (mbh->width == mode_info_block.x_resolution &&
+ mbh->height == mode_info_block.y_resolution &&
+ mbh->depth == mode_info_block.bits_per_pixel )
+ {
+ mbi.vbe_mode = *mode_list;
+ break;
+ }
+ }
+
+ if (*mode_list == 0xFFFF && fallback != 0xFFFF)
+ mbi.vbe_mode = fallback;
+
+ }
+
+ if (debug)
+ {
+ grub_printf ("%s mode requested: %dx%dx%d\n",
+ (mbh->mode_type == 0 ? "VBE graphics" : "Text"),
+ mbh->width, mbh->height, mbh->depth);
+ grub_printf ("Mode selected: 0x%x\n", mbi.vbe_mode);
+ }
+
+ } /* end graphics request */
+
+ /* sets the header pointer to point to the beginning of the
+ buffer by default */
+ pu.aout = (struct exec *) buffer;
+
/* Use BUFFER as a linux kernel header, if the image is Linux zImage
or bzImage. */
lh = (struct linux_kernel_header *) buffer;
@@ -134,7 +181,7 @@
}
}
}
- else if (flags & MULTIBOOT_AOUT_KLUDGE)
+ else if (mbh->flags & MULTIBOOT_AOUT_KLUDGE)
{
pu.mb = (struct multiboot_header *) (buffer + i);
entry_addr = (entry_func) pu.mb->entry_addr;
@@ -397,7 +444,7 @@
if (exec_type) /* can be loaded like a.out */
{
- if (flags & MULTIBOOT_AOUT_KLUDGE)
+ if (mbh->flags & MULTIBOOT_AOUT_KLUDGE)
str = "-and-data";
printf (", loadaddr=0x%x, text%s=0x%x", cur_addr, str, text_len);
@@ -407,7 +454,7 @@
{
cur_addr += text_len;
- if (!(flags & MULTIBOOT_AOUT_KLUDGE))
+ if (!(mbh->flags & MULTIBOOT_AOUT_KLUDGE))
{
/* we have to align to a 4K boundary */
if (align_4k)
Index: stage2/builtins.c
===================================================================
RCS file: /cvsroot/grub/grub/stage2/builtins.c,v
retrieving revision 1.116
diff -u -r1.116 builtins.c
--- stage2/builtins.c 2001/11/12 06:57:29 1.116
+++ stage2/builtins.c 2001/11/21 16:22:02
@@ -326,6 +326,21 @@
case KERNEL_TYPE_MULTIBOOT:
/* Multiboot */
+
+ /* MB specs require this */
+ if (! (mbh->flags & MULTIBOOT_VIDEO_MODE))
+ mbi.flags &= ~MB_INFO_VIDEO_INFO;
+
+ /* Switch to video mode */
+ if (mbi.vbe_mode != 0x03 &&
+ (set_vbe_mode (mbi.vbe_mode) != 0x004F ||
+ get_vbe_mode_info (mbi.vbe_mode, &mode_info_block) != 0x004F))
+ {
+ /* fallback to text mode */
+ mbi.vbe_mode = 0x03;
+ set_vbe_mode(mbi.vbe_mode);
+ }
+
multi_boot ((int) entry_addr, (int) &mbi);
break;
@@ -4099,8 +4114,6 @@
testvbe_func (char *arg, int flags)
{
int mode_number;
- struct vbe_controller controller;
- struct vbe_mode mode;
if (! *arg)
{
@@ -4111,26 +4124,22 @@
if (! safe_parse_maxint (&arg, &mode_number))
return 1;
- /* Preset `VBE2'. */
- grub_memmove (controller.signature, "VBE2", 4);
-
- /* Detect VBE BIOS. */
- if (get_vbe_controller_info (&controller) != 0x004F)
+ if (! (mbi.flags & MB_INFO_VIDEO_INFO))
{
grub_printf (" VBE BIOS is not present.\n");
return 0;
}
- if (controller.version < 0x0200)
+ if (vbe_info_block.version < 0x0200)
{
grub_printf (" VBE version %d.%d is not supported.\n",
- (int) (controller.version >> 8),
- (int) (controller.version & 0xFF));
+ (int) (vbe_info_block.version >> 8),
+ (int) (vbe_info_block.version & 0xFF));
return 0;
}
- if (get_vbe_mode_info (mode_number, &mode) != 0x004F
- || (mode.mode_attributes & 0x0091) != 0x0091)
+ if (get_vbe_mode_info (mode_number, &mode_info_block) != 0x004F
+ || (mode_info_block.mode_attributes & 0x0091) != 0x0091)
{
grub_printf (" Mode 0x%x is not supported.\n", mode_number);
return 0;
@@ -4145,13 +4154,13 @@
/* Draw something on the screen... */
{
- unsigned char *base_buf = (unsigned char *) mode.phys_base;
- int scanline = controller.version >= 0x0300
- ? mode.linear_bytes_per_scanline : mode.bytes_per_scanline;
+ unsigned char *base_buf = (unsigned char *) mode_info_block.phys_base;
+ int scanline = vbe_info_block.version >= 0x0300
+ ? mode_info_block.linear_bytes_per_scanline : mode_info_block.bytes_per_scanline;
/* FIXME: this assumes that any depth is a modulo of 8. */
- int bpp = mode.bits_per_pixel / 8;
- int width = mode.x_resolution;
- int height = mode.y_resolution;
+ int bpp = mode_info_block.bits_per_pixel / 8;
+ int width = mode_info_block.x_resolution;
+ int height = mode_info_block.y_resolution;
int x, y;
unsigned color = 0;
@@ -4319,70 +4328,53 @@
static int
vbeprobe_func (char *arg, int flags)
{
- struct vbe_controller controller;
- unsigned short *mode_list;
int mode_number = -1;
int count = 1;
- auto unsigned long vbe_far_ptr_to_linear (unsigned long);
-
- unsigned long vbe_far_ptr_to_linear (unsigned long ptr)
- {
- unsigned short seg = (ptr >> 16);
- unsigned short off = (ptr & 0xFFFF);
-
- return (seg << 4) + off;
- }
-
if (*arg)
{
if (! safe_parse_maxint (&arg, &mode_number))
return 1;
}
- /* Set the signature to `VBE2', to obtain VBE 3.0 information. */
- grub_memmove (controller.signature, "VBE2", 4);
-
- if (get_vbe_controller_info (&controller) != 0x004F)
+ if (! (mbi.flags & MB_INFO_VIDEO_INFO))
{
grub_printf (" VBE BIOS is not present.\n");
return 0;
}
/* Check the version. */
- if (controller.version < 0x0200)
+ if (vbe_info_block.version < 0x0200)
{
grub_printf (" VBE version %d.%d is not supported.\n",
- (int) (controller.version >> 8),
- (int) (controller.version & 0xFF));
+ (int) (vbe_info_block.version >> 8),
+ (int) (vbe_info_block.version & 0xFF));
return 0;
}
/* Print some information. */
grub_printf (" VBE version %d.%d\n",
- (int) (controller.version >> 8),
- (int) (controller.version & 0xFF));
+ (int) (vbe_info_block.version >> 8),
+ (int) (vbe_info_block.version & 0xFF));
/* Iterate probing modes. */
for (mode_list
- = (unsigned short *) vbe_far_ptr_to_linear (controller.video_mode);
+ = (unsigned short *) VBE_FAR_PTR (vbe_info_block.video_mode);
*mode_list != 0xFFFF;
mode_list++)
{
- struct vbe_mode mode;
-
- if (get_vbe_mode_info (*mode_list, &mode) != 0x004F)
+ if (get_vbe_mode_info (*mode_list, &mode_info_block) != 0x004F)
continue;
/* Skip this, if this is not supported or linear frame buffer
mode is not support. */
- if ((mode.mode_attributes & 0x0081) != 0x0081)
+ if ((mode_info_block.mode_attributes & 0x0001) != 0x0001)
continue;
if (mode_number == -1 || mode_number == *mode_list)
{
char *model;
- switch (mode.memory_model)
+ switch (mode_info_block.memory_model)
{
case 0x00: model = "Text"; break;
case 0x01: model = "CGA graphics"; break;
@@ -4398,9 +4390,9 @@
grub_printf (" 0x%x: %s, %ux%ux%u\n",
(unsigned) *mode_list,
model,
- (unsigned) mode.x_resolution,
- (unsigned) mode.y_resolution,
- (unsigned) mode.bits_per_pixel);
+ (unsigned) mode_info_block.x_resolution,
+ (unsigned) mode_info_block.y_resolution,
+ (unsigned) mode_info_block.bits_per_pixel);
if (mode_number != -1)
break;
@@ -4429,8 +4421,8 @@
vbeprobe_func,
BUILTIN_CMDLINE,
"vbeprobe [MODE]",
- "Probe VBE information. If the mode number MODE is specified, show only"
- "the information about only the mode."
+ "Probe VBE information. If the mode number MODE is specified, show "
+ "information about that mode only."
};
Index: stage2/common.c
===================================================================
RCS file: /cvsroot/grub/grub/stage2/common.c,v
retrieving revision 1.19
diff -u -r1.19 common.c
--- stage2/common.c 2001/11/12 06:57:29 1.19
+++ stage2/common.c 2001/11/21 16:22:02
@@ -29,6 +29,9 @@
*/
struct multiboot_info mbi;
+struct vbe_controller vbe_info_block;
+struct vbe_mode mode_info_block;
+unsigned short *mode_list;
unsigned long saved_drive;
unsigned long saved_partition;
#ifndef STAGE1_5
@@ -316,19 +319,29 @@
/* Get the APM BIOS table. */
get_apm_info ();
if (apm_bios_info.version)
+ {
+ mbi.flags |= MB_INFO_APM_TABLE;
mbi.apm_table = (unsigned long) &apm_bios_info;
+ }
+
+ /* Set the signature to `VBE2', to obtain VBE 3.0 information. */
+ grub_memmove (vbe_info_block.signature, "VBE2", 4);
+
+ if (get_vbe_controller_info (&vbe_info_block) == 0x004F)
+ {
+ mbi.flags |= MB_INFO_VIDEO_INFO;
+ mbi.vbe_control_info = (unsigned long) &vbe_info_block;
+ mbi.vbe_mode_info = (unsigned long) &mode_info_block;
+ }
/*
* Initialize other Multiboot Info flags.
*/
- mbi.flags = (MB_INFO_MEMORY | MB_INFO_CMDLINE | MB_INFO_BOOTDEV
+ mbi.flags |= (MB_INFO_MEMORY | MB_INFO_CMDLINE | MB_INFO_BOOTDEV
| MB_INFO_DRIVE_INFO | MB_INFO_CONFIG_TABLE
| MB_INFO_BOOT_LOADER_NAME);
- if (apm_bios_info.version)
- mbi.flags |= MB_INFO_APM_TABLE;
-
#endif /* STAGE1_5 */
#ifdef SUPPORT_DISKLESS
Index: stage2/shared.h
===================================================================
RCS file: /cvsroot/grub/grub/stage2/shared.h,v
retrieving revision 1.79
diff -u -r1.79 shared.h
--- stage2/shared.h 2001/11/12 06:57:29 1.79
+++ stage2/shared.h 2001/11/21 16:22:02
@@ -388,6 +388,7 @@
#include "mb_header.h"
#include "mb_info.h"
+extern struct multiboot_header *mbh;
/* For the Linux/i386 boot protocol version 2.02. */
struct linux_kernel_header
@@ -662,6 +663,9 @@
*/
extern struct multiboot_info mbi;
+struct vbe_controller vbe_info_block;
+struct vbe_mode mode_info_block;
+extern unsigned short *mode_list;
extern unsigned long saved_drive;
extern unsigned long saved_partition;
#ifndef STAGE1_5
@@ -760,6 +764,9 @@
/* Set VBE mode. */
int set_vbe_mode (int mode_number);
+
+/* Convert 32-bit pointer to 16-bit segment:offset style pointer */
+#define VBE_FAR_PTR(p) (((p >> 16) << 4) + (p & 0xFFFF))
/* Return the data area immediately following our code. */
int get_code_end (void);