qemu/pc-bios/optionrom/multiboot.S
<<
>>
Prefs
   1/*
   2 * Multiboot Option ROM
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License as published by
   6 * the Free Software Foundation; either version 2 of the License, or
   7 * (at your option) any later version.
   8 *
   9 * This program is distributed in the hope that it will be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12 * GNU General Public License for more details.
  13 *
  14 * You should have received a copy of the GNU General Public License
  15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
  16 *
  17 * Copyright Novell Inc, 2009
  18 *   Authors: Alexander Graf <agraf@suse.de>
  19 */
  20
  21#include "optionrom.h"
  22
  23#define BOOT_ROM_PRODUCT "multiboot loader"
  24
  25#define MULTIBOOT_MAGIC         0x2badb002
  26
  27#define GS_PROT_JUMP            0
  28#define GS_GDT_DESC             6
  29
  30
  31BOOT_ROM_START
  32
  33run_multiboot:
  34
  35        cli
  36        cld
  37
  38        mov             %cs, %eax
  39        shl             $0x4, %eax
  40
  41        /* set up a long jump descriptor that is PC relative */
  42
  43        /* move stack memory to %gs */
  44        mov             %ss, %ecx
  45        shl             $0x4, %ecx
  46        mov             %esp, %ebx
  47        add             %ebx, %ecx
  48        sub             $0x20, %ecx
  49        sub             $0x30, %esp
  50        shr             $0x4, %ecx
  51        mov             %cx, %gs
  52
  53        /* now push the indirect jump descriptor there */
  54        mov             (prot_jump), %ebx
  55        add             %eax, %ebx
  56        movl            %ebx, %gs:GS_PROT_JUMP
  57        mov             $8, %bx
  58        movw            %bx, %gs:GS_PROT_JUMP + 4
  59
  60        /* fix the gdt descriptor to be PC relative */
  61        movw            (gdt_desc), %bx
  62        movw            %bx, %gs:GS_GDT_DESC
  63        movl            (gdt_desc+2), %ebx
  64        add             %eax, %ebx
  65        movl            %ebx, %gs:GS_GDT_DESC + 2
  66
  67        xor             %eax, %eax
  68        mov             %eax, %es
  69
  70        /* Read the bootinfo struct into RAM */
  71        read_fw_blob(FW_CFG_INITRD)
  72
  73        /* FS = bootinfo_struct */
  74        read_fw         FW_CFG_INITRD_ADDR
  75        shr             $4, %eax
  76        mov             %ax, %fs
  77
  78        /* Account for the EBDA in the multiboot structure's e801
  79         * map.
  80         */
  81        int             $0x12
  82        cwtl
  83        movl            %eax, %fs:4
  84
  85        /* ES = mmap_addr */
  86        mov             %fs:48, %eax
  87        shr             $4, %eax
  88        mov             %ax, %es
  89
  90        /* Initialize multiboot mmap structs using int 0x15(e820) */
  91        xor             %ebx, %ebx
  92        /* Start storing mmap data at %es:0 */
  93        xor             %edi, %edi
  94
  95mmap_loop:
  96        /* The multiboot entry size has offset -4, so leave some space */
  97        add             $4, %di
  98        /* entry size (mmap struct) & max buffer size (int15) */
  99        movl            $20, %ecx
 100        /* e820 */
 101        movl            $0x0000e820, %eax
 102        /* 'SMAP' magic */
 103        movl            $0x534d4150, %edx
 104        int             $0x15
 105
 106mmap_check_entry:
 107        /* Error or last entry already done? */
 108        jb              mmap_done
 109
 110mmap_store_entry:
 111        /* store entry size */
 112        /* old as(1) doesn't like this insn so emit the bytes instead:
 113        movl            %ecx, %es:-4(%edi)
 114        */
 115        .dc.b           0x26,0x67,0x66,0x89,0x4f,0xfc
 116
 117        /* %edi += entry_size, store as mbs_mmap_length */
 118        add             %ecx, %edi
 119        movw            %di, %fs:0x2c
 120
 121        /* Continuation value 0 means last entry */
 122        test            %ebx, %ebx
 123        jnz             mmap_loop
 124
 125mmap_done:
 126        /* Calculate upper_mem field: The amount of memory between 1 MB and
 127           the first upper memory hole. Get it from the mmap. */
 128        xor             %di, %di
 129        mov             $0x100000, %edx
 130upper_mem_entry:
 131        cmp             %fs:0x2c, %di
 132        je              upper_mem_done
 133        add             $4, %di
 134
 135        /* Skip if type != 1 */
 136        cmpl            $1, %es:16(%di)
 137        jne             upper_mem_next
 138
 139        /* Skip if > 4 GB */
 140        movl            %es:4(%di), %eax
 141        test            %eax, %eax
 142        jnz             upper_mem_next
 143
 144        /* Check for contiguous extension (base <= %edx < base + length) */
 145        movl            %es:(%di), %eax
 146        cmp             %eax, %edx
 147        jb              upper_mem_next
 148        addl            %es:8(%di), %eax
 149        cmp             %eax, %edx
 150        jae             upper_mem_next
 151
 152        /* If so, update %edx, and restart the search (mmap isn't ordered) */
 153        mov             %eax, %edx
 154        xor             %di, %di
 155        jmp             upper_mem_entry
 156
 157upper_mem_next:
 158        addl            %es:-4(%di), %edi
 159        jmp             upper_mem_entry
 160
 161upper_mem_done:
 162        sub             $0x100000, %edx
 163        shr             $10, %edx
 164        mov             %edx, %fs:0x8
 165
 166real_to_prot:
 167        /* Load the GDT before going into protected mode */
 168lgdt:
 169        data32 lgdt     %gs:GS_GDT_DESC
 170
 171        /* get us to protected mode now */
 172        movl            $1, %eax
 173        movl            %eax, %cr0
 174
 175        /* the LJMP sets CS for us and gets us to 32-bit */
 176ljmp:
 177        data32 ljmp     *%gs:GS_PROT_JUMP
 178
 179prot_mode:
 180.code32
 181
 182        /* initialize all other segments */
 183        movl            $0x10, %eax
 184        movl            %eax, %ss
 185        movl            %eax, %ds
 186        movl            %eax, %es
 187        movl            %eax, %fs
 188        movl            %eax, %gs
 189
 190        /* Read the kernel and modules into RAM */
 191        read_fw_blob(FW_CFG_KERNEL)
 192
 193        /* Jump off to the kernel */
 194        read_fw         FW_CFG_KERNEL_ENTRY
 195        mov             %eax, %ecx
 196
 197        /* EBX contains a pointer to the bootinfo struct */
 198        read_fw         FW_CFG_INITRD_ADDR
 199        movl            %eax, %ebx
 200
 201        /* EAX has to contain the magic */
 202        movl            $MULTIBOOT_MAGIC, %eax
 203ljmp2:
 204        jmp             *%ecx
 205
 206/* Variables */
 207.align 4, 0
 208prot_jump:      .long prot_mode
 209                .short 8
 210
 211.align 4, 0
 212gdt:
 213        /* 0x00 */
 214.byte   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 215
 216        /* 0x08: code segment (base=0, limit=0xfffff, type=32bit code exec/read, DPL=0, 4k) */
 217.byte   0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00
 218
 219        /* 0x10: data segment (base=0, limit=0xfffff, type=32bit data read/write, DPL=0, 4k) */
 220.byte   0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0xcf, 0x00
 221
 222        /* 0x18: code segment (base=0, limit=0x0ffff, type=16bit code exec/read/conf, DPL=0, 1b) */
 223.byte   0xff, 0xff, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00
 224
 225        /* 0x20: data segment (base=0, limit=0x0ffff, type=16bit data read/write, DPL=0, 1b) */
 226.byte   0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00
 227
 228gdt_desc:
 229.short  (5 * 8) - 1
 230.long   gdt
 231
 232BOOT_ROM_END
 233