Hi all,

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:

* Made the VBE structures 'controller' and 'mode' from the corresponding
  builtins global and renamed them to a more specific 'vbe_info_block' and 
  'vbe_mode_info', respectively.

* Turned vbe_far_pointer_to_linear into a macro VBE_FAR_PTR

* Moved the check for a compliant BIOS into init_bios_info() in common.c

* Added some code for the video selection to load_image() boot.c. Also, I
  have made the flags in the multiboot header available as a global variable
  mb_header_flags.

* Added some code to switch to the selected mode just before calling the
  kernel

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.

Comments anyone?

Cheers,

        Alex
diff -b -u --recursive grub-0.90/stage2/boot.c grub-0.90-local/stage2/boot.c
--- grub-0.90/stage2/boot.c	Thu Jul  5 12:52:59 2001
+++ grub-0.90-local/stage2/boot.c	Sun Nov 11 18:53:20 2001
@@ -30,6 +30,7 @@
 entry_func entry_addr;
 static struct mod_list mll[99];
 
+unsigned long mb_header_flags;
 
 /*
  *  The next two functions, 'load_image' and 'load_module', are the building
@@ -44,7 +45,7 @@
   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
@@ -58,10 +59,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;
 
@@ -79,8 +76,9 @@
     {
       if (MULTIBOOT_FOUND ((int) (buffer + i), len - i))
 	{
-	  flags = ((struct multiboot_header *) (buffer + i))->flags;
-	  if (flags & MULTIBOOT_UNSUPPORTED)
+	  pu.mb = (struct multiboot_header *) (buffer + i);
+	  mb_header_flags = pu.mb->flags;
+	  if (mb_header_flags & MULTIBOOT_UNSUPPORTED)
 	    {
 	      grub_close ();
 	      errnum = ERR_BOOT_FEATURES;
@@ -92,6 +90,56 @@
 	}
     }
 
+  /* Handle graphics request for multiboot kernels */
+  if (type == KERNEL_TYPE_MULTIBOOT &&
+      mb_header_flags & MULTIBOOT_VIDEO_MODE &&
+      mbi.flags & MB_INFO_VIDEO_INFO)
+    {
+      mbi.vbe_mode = 0x03;
+
+      if (pu.mb->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;
+
+	      if (fallback == 0xFFFF) fallback = *mode_list;
+
+	      if (pu.mb->width == mode_info_block.x_resolution &&
+		  pu.mb->height == mode_info_block.y_resolution &&
+		  pu.mb->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",
+		       (pu.mb->mode_type == 0 ? "VBE graphics" : "Text"),
+		       pu.mb->width, pu.mb->height, pu.mb->depth);
+	  grub_printf ("Mode selected: 0x%x\n", mbi.vbe_mode);
+	}
+
+    }
+
+  /* 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;
@@ -135,7 +183,7 @@
 	    }
 	}
     }
-  else if (flags & MULTIBOOT_AOUT_KLUDGE)
+  else if (mb_header_flags & MULTIBOOT_AOUT_KLUDGE)
     {
       pu.mb = (struct multiboot_header *) (buffer + i);
       entry_addr = (entry_func) pu.mb->entry_addr;
@@ -381,6 +429,7 @@
       return KERNEL_TYPE_NONE;
     }
 
+
   /* fill the multiboot info structure */
   mbi.cmdline = (int) arg;
   mbi.mods_count = 0;
@@ -398,7 +447,7 @@
 
   if (exec_type)		/* can be loaded like a.out */
     {
-      if (flags & MULTIBOOT_AOUT_KLUDGE)
+      if (mb_header_flags & MULTIBOOT_AOUT_KLUDGE)
 	str = "-and-data";
 
       printf (", loadaddr=0x%x, text%s=0x%x", cur_addr, str, text_len);
@@ -408,7 +457,7 @@
 	{
 	  cur_addr += text_len;
 
-	  if (!(flags & MULTIBOOT_AOUT_KLUDGE))
+	  if (!(mb_header_flags & MULTIBOOT_AOUT_KLUDGE))
 	    {
 	      /* we have to align to a 4K boundary */
 	      if (align_4k)
diff -b -u --recursive grub-0.90/stage2/builtins.c grub-0.90-local/stage2/builtins.c
--- grub-0.90/stage2/builtins.c	Fri Jun 22 01:15:02 2001
+++ grub-0.90-local/stage2/builtins.c	Sun Nov 11 19:04:37 2001
@@ -327,6 +327,21 @@
 
     case KERNEL_TYPE_MULTIBOOT:
       /* Multiboot */
+
+      /* MB specs require this */
+      if (! (mb_header_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;
 
@@ -4087,8 +4102,6 @@
 testvbe_func (char *arg, int flags)
 {
   int mode_number;
-  struct vbe_controller controller;
-  struct vbe_mode mode;
   
   if (! *arg)
     {
@@ -4099,26 +4112,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;
@@ -4133,13 +4142,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;
 
@@ -4307,70 +4316,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;
@@ -4386,9 +4378,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;
@@ -4417,8 +4409,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."
 };
   
 
diff -b -u --recursive grub-0.90/stage2/common.c grub-0.90-local/stage2/common.c
--- grub-0.90/stage2/common.c	Sun Nov 26 20:31:30 2000
+++ grub-0.90-local/stage2/common.c	Sun Nov 11 17:52:05 2001
@@ -30,6 +30,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
diff -b -u --recursive grub-0.90/stage2/shared.h grub-0.90-local/stage2/shared.h
--- grub-0.90/stage2/shared.h	Fri Jun 22 04:32:56 2001
+++ grub-0.90-local/stage2/shared.h	Sun Nov 11 17:36:11 2001
@@ -383,6 +383,7 @@
 
 #include "mb_header.h"
 #include "mb_info.h"
+extern unsigned long mb_header_flags;
 
 /* For the Linux/i386 boot protocol version 2.02.  */
 struct linux_kernel_header
@@ -656,6 +657,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
@@ -754,6 +758,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);

Reply via email to