## 4 Examples ¶

Caution: The following items are not part of the specification document, but are included for prospective operating system and boot loader writers.

### 4.1 Notes on PC ¶

In reference to bit 0 of the ‘`flags`’ parameter in the Multiboot information structure, if the bootloader in question uses older BIOS interfaces, or the newest ones are not available (see description about bit 6), then a maximum of either 15 or 63 megabytes of memory may be reported. It is highly recommended that boot loaders perform a thorough memory probe.

In reference to bit 1 of the ‘`flags`’ parameter in the Multiboot information structure, it is recognized that determination of which BIOS drive maps to which device driver in an operating system is non-trivial, at best. Many kludges have been made to various operating systems instead of solving this problem, most of them breaking under many conditions. To encourage the use of general-purpose solutions to this problem, there are 2 BIOS device mapping techniques (see BIOS device mapping techniques).

In reference to bit 6 of the ‘`flags`’ parameter in the Multiboot information structure, it is important to note that the data structure used there (starting with ‘`BaseAddrLow`’) is the data returned by the INT 15h, AX=E820h — Query System Address Map call. See See Query System Address Map in The GRUB Manual, for more information. The interface here is meant to allow a boot loader to work unmodified with any reasonable extensions of the BIOS interface, passing along any extra data to be interpreted by the operating system as desired.

### 4.2 BIOS device mapping techniques ¶

Both of these techniques should be usable from any PC operating system, and neither require any special support in the drivers themselves. This section will be flushed out into detailed explanations, particularly for the I/O restriction technique.

The general rule is that the data comparison technique is the quick and dirty solution. It works most of the time, but doesn’t cover all the bases, and is relatively simple.

The I/O restriction technique is much more complex, but it has potential to solve the problem under all conditions, plus allow access of the remaining BIOS devices when not all of them have operating system drivers.

#### 4.2.1 Data comparison technique ¶

Before activating any of the device drivers, gather enough data from similar sectors on each of the disks such that each one can be uniquely identified.

After activating the device drivers, compare data from the drives using the operating system drivers. This should hopefully be sufficient to provide such a mapping.

Problems:

1. The data on some BIOS devices might be identical (so the part reading the drives from the BIOS should have some mechanism to give up).
2. There might be extra drives not accessible from the BIOS which are identical to some drive used by the BIOS (so it should be capable of giving up there as well).

#### 4.2.2 I/O restriction technique ¶

This first step may be unnecessary, but first create copy-on-write mappings for the device drivers writing into PC RAM. Keep the original copies for the clean BIOS virtual machine to be created later.

For each device driver brought online, determine which BIOS devices become inaccessible by:

1. Create a clean BIOS virtual machine.
2. Set the I/O permission map for the I/O area claimed by the device driver to no permissions (neither read nor write).
3. Access each device.
4. Record which devices succeed, and those which try to access the restricted I/O areas (hopefully, this will be an xor situation).

For each device driver, given how many of the BIOS devices were subsumed by it (there should be no gaps in this list), it should be easy to determine which devices on the controller these are.

In general, you have at most 2 disks from each controller given BIOS numbers, but they pretty much always count from the lowest logically numbered devices on the controller.

### 4.3 Example OS code ¶

In this distribution, the example Multiboot kernel `kernel` is included. The kernel just prints out the Multiboot information structure on the screen, so you can make use of the kernel to test a Multiboot-compliant boot loader and for reference to how to implement a Multiboot kernel. The source files can be found under the directory `doc` in the Multiboot source distribution.

The kernel `kernel` consists of only three files: `boot.S`, `kernel.c` and `multiboot.h`. The assembly source `boot.S` is written in GAS (see GNU assembler in The GNU assembler), and contains the Multiboot information structure to comply with the specification. When a Multiboot-compliant boot loader loads and execute it, it initialize the stack pointer and `EFLAGS`, and then call the function `cmain` defined in `kernel.c`. If `cmain` returns to the callee, then it shows a message to inform the user of the halt state and stops forever until you push the reset key. The file `kernel.c` contains the function `cmain`, which checks if the magic number passed by the boot loader is valid and so on, and some functions to print messages on the screen. The file `multiboot.h` defines some macros, such as the magic number for the Multiboot header, the Multiboot header structure and the Multiboot information structure.

#### 4.3.1 multiboot.h ¶

This is the source code in the file `multiboot.h`:

    /* multiboot.h - Multiboot header file. */
    /* Copyright (C) 1999,2003,2007,2008,2009,2010  Free Software Foundation, Inc.
     *
     *  Permission is hereby granted, free of charge, to any person obtaining a copy
     *  of this software and associated documentation files (the "Software"), to
     *  deal in the Software without restriction, including without limitation the
     *  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
     *  sell copies of the Software, and to permit persons to whom the Software is
     *  furnished to do so, subject to the following conditions:
     *
     *  The above copyright notice and this permission notice shall be included in
     *  all copies or substantial portions of the Software.
     *
     *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL ANY
     *  DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
     *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
     *  IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     */

    #ifndef MULTIBOOT_HEADER
    #define MULTIBOOT_HEADER 1

    /* How many bytes from the start of the file we search for the header. */
    #define MULTIBOOT_SEARCH                        8192
    #define MULTIBOOT_HEADER_ALIGN                  4

    /* The magic field should contain this. */
    #define MULTIBOOT_HEADER_MAGIC                  0x1BADB002

    /* This should be in %eax. */
    #define MULTIBOOT_BOOTLOADER_MAGIC              0x2BADB002

    /* Alignment of multiboot modules. */
    #define MULTIBOOT_MOD_ALIGN                     0x00001000

    /* Alignment of the multiboot info structure. */
    #define MULTIBOOT_INFO_ALIGN                    0x00000004

    /* Flags set in the ’flags’ member of the multiboot header. */

    /* Align all boot modules on i386 page (4KB) boundaries. */
    #define MULTIBOOT_PAGE_ALIGN                    0x00000001

    /* Must pass memory information to OS. */
    #define MULTIBOOT_MEMORY_INFO                   0x00000002

    /* Must pass video information to OS. */
    #define MULTIBOOT_VIDEO_MODE                    0x00000004

    /* This flag indicates the use of the address fields in the header. */
    #define MULTIBOOT_AOUT_KLUDGE                   0x00010000

    /* Flags to be set in the ’flags’ member of the multiboot info structure. */

    /* is there basic lower/upper memory information? */
    #define MULTIBOOT_INFO_MEMORY                   0x00000001
    /* is there a boot device set? */
    #define MULTIBOOT_INFO_BOOTDEV                  0x00000002
    /* is the command-line defined? */
    #define MULTIBOOT_INFO_CMDLINE                  0x00000004
    /* are there modules to do something with? */
    #define MULTIBOOT_INFO_MODS                     0x00000008

    /* These next two are mutually exclusive */

    /* is there a symbol table loaded? */
    #define MULTIBOOT_INFO_AOUT_SYMS                0x00000010
    /* is there an ELF section header table? */
    #define MULTIBOOT_INFO_ELF_SHDR                 0X00000020

    /* is there a full memory map? */
    #define MULTIBOOT_INFO_MEM_MAP                  0x00000040

    /* Is there drive info? */
    #define MULTIBOOT_INFO_DRIVE_INFO               0x00000080

    /* Is there a config table? */
    #define MULTIBOOT_INFO_CONFIG_TABLE             0x00000100

    /* Is there a boot loader name? */
    #define MULTIBOOT_INFO_BOOT_LOADER_NAME         0x00000200

    /* Is there a APM table? */
    #define MULTIBOOT_INFO_APM_TABLE                0x00000400

    /* Is there video information? */
    #define MULTIBOOT_INFO_VBE_INFO                 0x00000800
    #define MULTIBOOT_INFO_FRAMEBUFFER_INFO         0x00001000

    #ifndef ASM_FILE

    typedef unsigned char           multiboot_uint8_t;
    typedef unsigned short          multiboot_uint16_t;
    typedef unsigned int            multiboot_uint32_t;
    typedef unsigned long long      multiboot_uint64_t;

    struct multiboot_header
    {
      /* Must be MULTIBOOT_MAGIC - see above. */
      multiboot_uint32_t magic;

      /* Feature flags. */
      multiboot_uint32_t flags;

      /* The above fields plus this one must equal 0 mod 2^32. */
      multiboot_uint32_t checksum;

      /* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */
      multiboot_uint32_t header_addr;
      multiboot_uint32_t load_addr;
      multiboot_uint32_t load_end_addr;
      multiboot_uint32_t bss_end_addr;
      multiboot_uint32_t entry_addr;

      /* These are only valid if MULTIBOOT_VIDEO_MODE is set. */
      multiboot_uint32_t mode_type;
      multiboot_uint32_t width;
      multiboot_uint32_t height;
      multiboot_uint32_t depth;
    };

    /* The symbol table for a.out. */
    struct multiboot_aout_symbol_table
    {
      multiboot_uint32_t tabsize;
      multiboot_uint32_t strsize;
      multiboot_uint32_t addr;
      multiboot_uint32_t reserved;
    };
    typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t;

    /* The section header table for ELF. */
    struct multiboot_elf_section_header_table
    {
      multiboot_uint32_t num;
      multiboot_uint32_t size;
      multiboot_uint32_t addr;
      multiboot_uint32_t shndx;
    };
    typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t;

    struct multiboot_info
    {
      /* Multiboot info version number */
      multiboot_uint32_t flags;

      /* Available memory from BIOS */
      multiboot_uint32_t mem_lower;
      multiboot_uint32_t mem_upper;

      /* "root" partition */
      multiboot_uint32_t boot_device;

      /* Kernel command line */
      multiboot_uint32_t cmdline;

      /* Boot-Module list */
      multiboot_uint32_t mods_count;
      multiboot_uint32_t mods_addr;

      union
      {
        multiboot_aout_symbol_table_t aout_sym;
        multiboot_elf_section_header_table_t elf_sec;
      } u;

      /* Memory Mapping buffer */
      multiboot_uint32_t mmap_length;
      multiboot_uint32_t mmap_addr;

      /* Drive Info buffer */
      multiboot_uint32_t drives_length;
      multiboot_uint32_t drives_addr;

      /* ROM configuration table */
      multiboot_uint32_t config_table;

      /* Boot Loader Name */
      multiboot_uint32_t boot_loader_name;

      /* APM table */
      multiboot_uint32_t apm_table;

      /* Video */
      multiboot_uint32_t vbe_control_info;
      multiboot_uint32_t vbe_mode_info;
      multiboot_uint16_t vbe_mode;
      multiboot_uint16_t vbe_interface_seg;
      multiboot_uint16_t vbe_interface_off;
      multiboot_uint16_t vbe_interface_len;

      multiboot_uint64_t framebuffer_addr;
      multiboot_uint32_t framebuffer_pitch;
      multiboot_uint32_t framebuffer_width;
      multiboot_uint32_t framebuffer_height;
      multiboot_uint8_t framebuffer_bpp;
    #define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0
    #define MULTIBOOT_FRAMEBUFFER_TYPE_RGB     1
    #define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT     2
      multiboot_uint8_t framebuffer_type;
      union
      {
        struct
        {
          multiboot_uint32_t framebuffer_palette_addr;
          multiboot_uint16_t framebuffer_palette_num_colors;
        };
        struct
        {
          multiboot_uint8_t framebuffer_red_field_position;
          multiboot_uint8_t framebuffer_red_mask_size;
          multiboot_uint8_t framebuffer_green_field_position;
          multiboot_uint8_t framebuffer_green_mask_size;
          multiboot_uint8_t framebuffer_blue_field_position;
          multiboot_uint8_t framebuffer_blue_mask_size;
        };
      };
    };
    typedef struct multiboot_info multiboot_info_t;

    struct multiboot_color
    {
      multiboot_uint8_t red;
      multiboot_uint8_t green;
      multiboot_uint8_t blue;
    };

    struct multiboot_mmap_entry
    {
      multiboot_uint32_t size;
      multiboot_uint64_t addr;
      multiboot_uint64_t len;
    #define MULTIBOOT_MEMORY_AVAILABLE              1
    #define MULTIBOOT_MEMORY_RESERVED               2
    #define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE       3
    #define MULTIBOOT_MEMORY_NVS                    4
    #define MULTIBOOT_MEMORY_BADRAM                 5
      multiboot_uint32_t type;
    } __attribute__((packed));
    typedef struct multiboot_mmap_entry multiboot_memory_map_t;

    struct multiboot_mod_list
    {
      /* the memory used goes from bytes ’mod_start’ to ’mod_end-1’ inclusive */
      multiboot_uint32_t mod_start;
      multiboot_uint32_t mod_end;

      /* Module command line */
      multiboot_uint32_t cmdline;

      /* padding to take it to 16 bytes (must be zero) */
      multiboot_uint32_t pad;
    };
    typedef struct multiboot_mod_list multiboot_module_t;

    /* APM BIOS info. */
    struct multiboot_apm_info
    {
      multiboot_uint16_t version;
      multiboot_uint16_t cseg;
      multiboot_uint32_t offset;
      multiboot_uint16_t cseg_16;
      multiboot_uint16_t dseg;
      multiboot_uint16_t flags;
      multiboot_uint16_t cseg_len;
      multiboot_uint16_t cseg_16_len;
      multiboot_uint16_t dseg_len;
    };

    #endif /* ! ASM_FILE */

    #endif /* ! MULTIBOOT_HEADER */

#### 4.3.2 boot.S ¶

In the file `boot.S`:

    /* boot.S - bootstrap the kernel */
    /* Copyright (C) 1999, 2001, 2010  Free Software Foundation, Inc.
     *
     * This program is free software: you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation, either version 3 of the License, or
     * (at your option) any later version.
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     *
     * You should have received a copy of the GNU General Public License
     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     */

    #define ASM_FILE        1
    #include <multiboot.h>

    /* C symbol format. HAVE_ASM_USCORE is defined by configure. */
    #ifdef HAVE_ASM_USCORE
    # define EXT_C(sym)                     _ ## sym
    #else
    # define EXT_C(sym)                     sym
    #endif

    /* The size of our stack (16KB). */
    #define STACK_SIZE                      0x4000

    /* The flags for the Multiboot header. */
    #ifdef __ELF__
    # define AOUT_KLUDGE 0
    #else
    # define AOUT_KLUDGE MULTIBOOT_AOUT_KLUDGE
    #endif
    #define MULTIBOOT_HEADER_FLAGS                  MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_VIDEO_MODE | AOUT_KLUDGE

            .text

            .globl  start, _start
    start:
    _start:
            jmp     multiboot_entry

            /* Align 32 bits boundary. */
            .align  4

            /* Multiboot header. */
    multiboot_header:
            /* magic */
            .long   MULTIBOOT_HEADER_MAGIC
            /* flags */
            .long   MULTIBOOT_HEADER_FLAGS
            /* checksum */
            .long   -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
    #ifndef __ELF__
            /* header_addr */
            .long   multiboot_header
            /* load_addr */
            .long   _start
            /* load_end_addr */
            .long   _edata
            /* bss_end_addr */
            .long   _end
            /* entry_addr */
            .long   multiboot_entry
    #else /* ! __ELF__ */
            .long   0
            .long   0
            .long   0
            .long   0
            .long   0
    #endif /* __ELF__ */
            .long 0
            .long 1024
            .long 768
            .long 32

    multiboot_entry:
            /* Initialize the stack pointer. */
            movl    $(stack + STACK_SIZE), %esp

            /* Reset EFLAGS. */
            pushl   $0
            popf

            /* Push the pointer to the Multiboot information structure. */
            pushl   %ebx
            /* Push the magic value. */
            pushl   %eax

            /* Now enter the C main function... */
            call    EXT_C(cmain)

            /* Halt. */
            pushl   $halt_message
            call    EXT_C(printf)

    loop:   hlt
            jmp     loop

    halt_message:
            .asciz  "Halted."

            /* Our stack area. */
            .comm   stack, STACK_SIZE

#### 4.3.3 kernel.c ¶

And, in the file `kernel.c`:

    /* kernel.c - the C part of the kernel */
    /* Copyright (C) 1999, 2010  Free Software Foundation, Inc.
     *
     * This program is free software: you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation, either version 3 of the License, or
     * (at your option) any later version.
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     *
     * You should have received a copy of the GNU General Public License
     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     */

    #include <multiboot.h>

    /* Macros. */

    /* Check if the bit BIT in FLAGS is set. */
    #define CHECK_FLAG(flags,bit)   ((flags) & (1 << (bit)))

    /* Some screen stuff. */
    /* The number of columns. */
    #define COLUMNS                 80
    /* The number of lines. */
    #define LINES                   24
    /* The attribute of an character. */
    #define ATTRIBUTE               7
    /* The video memory address. */
    #define VIDEO                   0xB8000

    /* Variables. */
    /* Save the X position. */
    static int xpos;
    /* Save the Y position. */
    static int ypos;
    /* Point to the video memory. */
    static volatile unsigned char *video;

    /* Forward declarations. */
    void cmain (unsigned long magic, unsigned long addr);
    static void cls (void);
    static void itoa (char *buf, int base, int d);
    static void putchar (int c);
    void printf (const char *format, ...);

    /* Check if MAGIC is valid and print the Multiboot information structure
       pointed by ADDR. */
    void
    cmain (unsigned long magic, unsigned long addr)
    {
      multiboot_info_t *mbi;

      /* Clear the screen. */
      cls ();

      /* Am I booted by a Multiboot-compliant boot loader? */
      if (magic != MULTIBOOT_BOOTLOADER_MAGIC)
        {
          printf ("Invalid magic number: 0x%x\n", (unsigned) magic);
          return;
        }

      /* Set MBI to the address of the Multiboot information structure. */
      mbi = (multiboot_info_t *) addr;

      /* Print out the flags. */
      printf ("flags = 0x%x\n", (unsigned) mbi->flags);

      /* Are mem_* valid? */
      if (CHECK_FLAG (mbi->flags, 0))
        printf ("mem_lower = %uKB, mem_upper = %uKB\n",
                (unsigned) mbi->mem_lower, (unsigned) mbi->mem_upper);

      /* Is boot_device valid? */
      if (CHECK_FLAG (mbi->flags, 1))
        printf ("boot_device = 0x%x\n", (unsigned) mbi->boot_device);

      /* Is the command line passed? */
      if (CHECK_FLAG (mbi->flags, 2))
        printf ("cmdline = %s\n", (char *) mbi->cmdline);

      /* Are mods_* valid? */
      if (CHECK_FLAG (mbi->flags, 3))
        {
          multiboot_module_t *mod;
          int i;

          printf ("mods_count = %d, mods_addr = 0x%x\n",
                  (int) mbi->mods_count, (int) mbi->mods_addr);
          for (i = 0, mod = (multiboot_module_t *) mbi->mods_addr;
               i < mbi->mods_count;
               i++, mod++)
            printf (" mod_start = 0x%x, mod_end = 0x%x, cmdline = %s\n",
                    (unsigned) mod->mod_start,
                    (unsigned) mod->mod_end,
                    (char *) mod->cmdline);
        }

      /* Bits 4 and 5 are mutually exclusive! */
      if (CHECK_FLAG (mbi->flags, 4) && CHECK_FLAG (mbi->flags, 5))
        {
          printf ("Both bits 4 and 5 are set.\n");
          return;
        }

      /* Is the symbol table of a.out valid? */
      if (CHECK_FLAG (mbi->flags, 4))
        {
          multiboot_aout_symbol_table_t *multiboot_aout_sym = &(mbi->u.aout_sym);

          printf ("multiboot_aout_symbol_table: tabsize = 0x%0x, "
                  "strsize = 0x%x, addr = 0x%x\n",
                  (unsigned) multiboot_aout_sym->tabsize,
                  (unsigned) multiboot_aout_sym->strsize,
                  (unsigned) multiboot_aout_sym->addr);
        }

      /* Is the section header table of ELF valid? */
      if (CHECK_FLAG (mbi->flags, 5))
        {
          multiboot_elf_section_header_table_t *multiboot_elf_sec = &(mbi->u.elf_sec);

          printf ("multiboot_elf_sec: num = %u, size = 0x%x,"
                  " addr = 0x%x, shndx = 0x%x\n",
                  (unsigned) multiboot_elf_sec->num, (unsigned) multiboot_elf_sec->size,
                  (unsigned) multiboot_elf_sec->addr, (unsigned) multiboot_elf_sec->shndx);
        }

      /* Are mmap_* valid? */
      if (CHECK_FLAG (mbi->flags, 6))
        {
          multiboot_memory_map_t *mmap;

          printf ("mmap_addr = 0x%x, mmap_length = 0x%x\n",
                  (unsigned) mbi->mmap_addr, (unsigned) mbi->mmap_length);
          for (mmap = (multiboot_memory_map_t *) mbi->mmap_addr;
               (unsigned long) mmap < mbi->mmap_addr + mbi->mmap_length;
               mmap = (multiboot_memory_map_t *) ((unsigned long) mmap
                                        + mmap->size + sizeof (mmap->size)))
            printf (" size = 0x%x, base_addr = 0x%x%08x,"
                    " length = 0x%x%08x, type = 0x%x\n",
                    (unsigned) mmap->size,
                    (unsigned) (mmap->addr >> 32),
                    (unsigned) (mmap->addr & 0xffffffff),
                    (unsigned) (mmap->len >> 32),
                    (unsigned) (mmap->len & 0xffffffff),
                    (unsigned) mmap->type);
        }

      /* Draw diagonal blue line. */
      if (CHECK_FLAG (mbi->flags, 12))
        {
          multiboot_uint32_t color;
          unsigned i;
          void *fb = (void *) (unsigned long) mbi->framebuffer_addr;

          switch (mbi->framebuffer_type)
            {
            case MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED:
              {
                unsigned best_distance, distance;
                struct multiboot_color *palette;

                palette = (struct multiboot_color *) mbi->framebuffer_palette_addr;

                color = 0;
                best_distance = 4*256*256;

                for (i = 0; i < mbi->framebuffer_palette_num_colors; i++)
                  {
                    distance = (0xff - palette[i].blue) * (0xff - palette[i].blue)
                      + palette[i].red * palette[i].red
                      + palette[i].green * palette[i].green;
                    if (distance < best_distance)
                      {
                        color = i;
                        best_distance = distance;
                      }
                  }
              }
              break;

            case MULTIBOOT_FRAMEBUFFER_TYPE_RGB:
              color = ((1 << mbi->framebuffer_blue_mask_size) - 1)
                << mbi->framebuffer_blue_field_position;
              break;

            case MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT:
              color = '\\' | 0x0100;
              break;

            default:
              color = 0xffffffff;
              break;
            }
          for (i = 0; i < mbi->framebuffer_width
                 && i < mbi->framebuffer_height; i++)
            {
              switch (mbi->framebuffer_bpp)
                {
                case 8:
                  {
                    multiboot_uint8_t *pixel = fb + mbi->framebuffer_pitch * i + i;
                    *pixel = color;
                  }
                  break;
                case 15:
                case 16:
                  {
                    multiboot_uint16_t *pixel
                      = fb + mbi->framebuffer_pitch * i + 2 * i;
                    *pixel = color;
                  }
                  break;
                case 24:
                  {
                    multiboot_uint32_t *pixel
                      = fb + mbi->framebuffer_pitch * i + 3 * i;
                    *pixel = (color & 0xffffff) | (*pixel & 0xff000000);
                  }
                  break;

                case 32:
                  {
                    multiboot_uint32_t *pixel
                      = fb + mbi->framebuffer_pitch * i + 4 * i;
                    *pixel = color;
                  }
                  break;
                }
            }
        }

    }

    /* Clear the screen and initialize VIDEO, XPOS and YPOS. */
    static void
    cls (void)
    {
      int i;

      video = (unsigned char *) VIDEO;

      for (i = 0; i < COLUMNS * LINES * 2; i++)
        *(video + i) = 0;

      xpos = 0;
      ypos = 0;
    }

    /* Convert the integer D to a string and save the string in BUF. If
       BASE is equal to ’d’, interpret that D is decimal, and if BASE is
       equal to ’x’, interpret that D is hexadecimal. */
    static void
    itoa (char *buf, int base, int d)
    {
      char *p = buf;
      char *p1, *p2;
      unsigned long ud = d;
      int divisor = 10;

      /* If %d is specified and D is minus, put ‘-’ in the head. */
      if (base == 'd' && d < 0)
        {
          *p++ = '-';
          buf++;
          ud = -d;
        }
      else if (base == 'x')
        divisor = 16;

      /* Divide UD by DIVISOR until UD == 0. */
      do
        {
          int remainder = ud % divisor;

          *p++ = (remainder < 10) ? remainder + '0' : remainder + 'a' - 10;
        }
      while (ud /= divisor);

      /* Terminate BUF. */
      *p = 0;

      /* Reverse BUF. */
      p1 = buf;
      p2 = p - 1;
      while (p1 < p2)
        {
          char tmp = *p1;
          *p1 = *p2;
          *p2 = tmp;
          p1++;
          p2--;
        }
    }

    /* Put the character C on the screen. */
    static void
    putchar (int c)
    {
      if (c == '\n' || c == '\r')
        {
        newline:
          xpos = 0;
          ypos++;
          if (ypos >= LINES)
            ypos = 0;
          return;
        }

      *(video + (xpos + ypos * COLUMNS) * 2) = c & 0xFF;
      *(video + (xpos + ypos * COLUMNS) * 2 + 1) = ATTRIBUTE;

      xpos++;
      if (xpos >= COLUMNS)
        goto newline;
    }

    /* Format a string and print it on the screen, just like the libc
       function printf. */
    void
    printf (const char *format, ...)
    {
      char **arg = (char **) &format;
      int c;
      char buf[20];

      arg++;

      while ((c = *format++) != 0)
        {
          if (c != '%')
            putchar (c);
          else
            {
              char *p, *p2;
              int pad0 = 0, pad = 0;

              c = *format++;
              if (c == '0')
                {
                  pad0 = 1;
                  c = *format++;
                }

              if (c >= '0' && c <= '9')
                {
                  pad = c - '0';
                  c = *format++;
                }

              switch (c)
                {
                case 'd':
                case 'u':
                case 'x':
                  itoa (buf, c, *((int *) arg++));
                  p = buf;
                  goto string;
                  break;

                case 's':
                  p = *arg++;
                  if (! p)
                    p = "(null)";

                string:
                  for (p2 = p; *p2; p2++);
                  for (; p2 < p + pad; p2++)
                    putchar (pad0 ? '0' : ' ');
                  while (*p)
                    putchar (*p++);
                  break;

                default:
                  putchar (*((int *) arg++));
                  break;
                }
            }
        }
    }

#### 4.3.4 Other Multiboot kernels ¶

Other useful information should be available in Multiboot kernels, such as GNU Mach and Fiasco http://os.inf.tu-dresden.de/fiasco/. And, it is worth mentioning the OSKit http://www.cs.utah.edu/projects/flux/oskit/, which provides a library supporting the specification.

### 4.4 Example boot loader code ¶

The GNU GRUB (see GRUB in The GRUB manual) project is a Multiboot-compliant boot loader, supporting all required and many optional features present in this specification. A public release has not been made, but the test release is available from:

ftp://alpha.gnu.org/gnu/grub

See the webpage http://www.gnu.org/software/grub/grub.html, for more information.