qemu/pc-bios/optionrom/linuxboot.S
<<
>>
Prefs
   1/*
   2 * Linux Boot 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 * Based on code in hw/pc.c.
  21 */
  22
  23#include "optionrom.h"
  24
  25#define BOOT_ROM_PRODUCT "Linux loader"
  26
  27BOOT_ROM_START
  28
  29run_linuxboot:
  30
  31        cli
  32        cld
  33
  34        jmp             copy_kernel
  35boot_kernel:
  36
  37        read_fw         FW_CFG_SETUP_ADDR
  38
  39        mov             %eax, %ebx
  40        shr             $4, %ebx
  41
  42        /* All segments contain real_addr */
  43        mov             %bx, %ds
  44        mov             %bx, %es
  45        mov             %bx, %fs
  46        mov             %bx, %gs
  47        mov             %bx, %ss
  48
  49        /* CX = CS we want to jump to */
  50        add             $0x20, %bx
  51        mov             %bx, %cx
  52
  53        /* SP = cmdline_addr-real_addr-16 */
  54        read_fw         FW_CFG_CMDLINE_ADDR
  55        mov             %eax, %ebx
  56        read_fw         FW_CFG_SETUP_ADDR
  57        sub             %eax, %ebx
  58        sub             $16, %ebx
  59        mov             %ebx, %esp
  60
  61        /* Build indirect lret descriptor */
  62        pushw           %cx             /* CS */
  63        xor             %ax, %ax
  64        pushw           %ax             /* IP = 0 */
  65
  66        /* Clear registers */
  67        xor             %eax, %eax
  68        xor             %ebx, %ebx
  69        xor             %ecx, %ecx
  70        xor             %edx, %edx
  71        xor             %edi, %edi
  72        xor             %ebp, %ebp
  73
  74        /* Jump to Linux */
  75        lret
  76
  77
  78copy_kernel:
  79        /* Read info block in low memory (0x10000 or 0x90000) */
  80        read_fw         FW_CFG_SETUP_ADDR
  81        shr             $4, %eax
  82        mov             %eax, %es
  83        xor             %edi, %edi
  84        read_fw_blob_addr32_edi(FW_CFG_SETUP)
  85
  86        cmpw            $0x203, %es:0x206      // if protocol >= 0x203
  87        jae             1f                     // have initrd_max
  88        movl            $0x37ffffff, %es:0x22c // else assume 0x37ffffff
  891:
  90
  91        /* Check if using kernel-specified initrd address */
  92        read_fw         FW_CFG_INITRD_ADDR
  93        mov             %eax, %edi             // (load_kernel wants it in %edi)
  94        read_fw         FW_CFG_INITRD_SIZE     // find end of initrd
  95        add             %edi, %eax
  96        xor             %es:0x22c, %eax        // if it matches es:0x22c
  97        and             $-4096, %eax           // (apart from padding for page)
  98        jz              load_kernel            // then initrd is not at top
  99                                               // of memory
 100
 101        /* pc.c placed the initrd at end of memory.  Compute a better
 102         * initrd address based on e801 data.
 103         */
 104        mov             $0xe801, %ax
 105        xor             %cx, %cx
 106        xor             %dx, %dx
 107        int             $0x15
 108
 109        /* Output could be in AX/BX or CX/DX */
 110        or              %cx, %cx
 111        jnz             1f
 112        or              %dx, %dx
 113        jnz             1f
 114        mov             %ax, %cx
 115        mov             %bx, %dx
 1161:
 117
 118        or              %dx, %dx
 119        jnz             2f
 120        addw            $1024, %cx            /* add 1 MB */
 121        movzwl          %cx, %edi
 122        shll            $10, %edi             /* convert to bytes */
 123        jmp             3f
 124
 1252:
 126        addw            $16777216 >> 16, %dx  /* add 16 MB */
 127        movzwl          %dx, %edi
 128        shll            $16, %edi             /* convert to bytes */
 129
 1303:
 131        read_fw         FW_CFG_INITRD_SIZE
 132        subl            %eax, %edi
 133        andl            $-4096, %edi          /* EDI = start of initrd */
 134        movl            %edi, %es:0x218       /* put it in the header */
 135
 136load_kernel:
 137        /* We need to load the kernel into memory we can't access in 16 bit
 138           mode, so let's get into 32 bit mode, write the kernel and jump
 139           back again. */
 140
 141        /* Reserve space on the stack for our GDT descriptor. */
 142        mov             %esp, %ebp
 143        sub             $16, %esp
 144
 145        /* Now create the GDT descriptor */
 146        movw            $((3 * 8) - 1), -16(%bp)
 147        mov             %cs, %eax
 148        movzwl          %ax, %eax
 149        shl             $4, %eax
 150        addl            $gdt, %eax
 151        movl            %eax, -14(%bp)
 152
 153        /* And load the GDT */
 154        data32 lgdt     -16(%bp)
 155        mov             %ebp, %esp
 156
 157        /* Get us to protected mode now */
 158        mov             $1, %eax
 159        mov             %eax, %cr0
 160
 161        /* So we can set ES to a 32-bit segment */
 162        mov             $0x10, %eax
 163        mov             %eax, %es
 164
 165        /* We're now running in 16-bit CS, but 32-bit ES! */
 166
 167        /* Load kernel and initrd */
 168        read_fw_blob_addr32_edi(FW_CFG_INITRD)
 169        read_fw_blob_addr32(FW_CFG_KERNEL)
 170        read_fw_blob_addr32(FW_CFG_CMDLINE)
 171
 172        /* And now jump into Linux! */
 173        mov             $0, %eax
 174        mov             %eax, %cr0
 175
 176        /* ES = CS */
 177        mov             %cs, %ax
 178        mov             %ax, %es
 179
 180        jmp             boot_kernel
 181
 182/* Variables */
 183
 184.align 4, 0
 185gdt:
 186        /* 0x00 */
 187.byte   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 188
 189        /* 0x08: code segment (base=0, limit=0xfffff, type=32bit code exec/read, DPL=0, 4k) */
 190.byte   0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00
 191
 192        /* 0x10: data segment (base=0, limit=0xfffff, type=32bit data read/write, DPL=0, 4k) */
 193.byte   0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0xcf, 0x00
 194
 195BOOT_ROM_END
 196