Alexander Graf | 57a46d0 | 2009-11-12 21:53:14 +0100 | [diff] [blame] | 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 | |
Gleb Natapov | 75b9f69 | 2011-01-31 15:11:01 +0200 | [diff] [blame] | 25 | #define BOOT_ROM_PRODUCT "Linux loader" |
| 26 | |
Alexander Graf | 57a46d0 | 2009-11-12 21:53:14 +0100 | [diff] [blame] | 27 | BOOT_ROM_START |
| 28 | |
| 29 | run_linuxboot: |
| 30 | |
| 31 | cli |
| 32 | cld |
| 33 | |
| 34 | jmp copy_kernel |
| 35 | boot_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 | |
| 78 | copy_kernel: |
Paolo Bonzini | 269e235 | 2014-12-11 02:17:03 +0100 | [diff] [blame] | 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 |
| 89 | 1: |
| 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 | */ |
Paolo Bonzini | cdebec5 | 2014-10-06 16:49:57 +0200 | [diff] [blame] | 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 |
| 116 | 1: |
| 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 | |
| 125 | 2: |
| 126 | addw $16777216 >> 16, %dx /* add 16 MB */ |
| 127 | movzwl %dx, %edi |
| 128 | shll $16, %edi /* convert to bytes */ |
| 129 | |
| 130 | 3: |
| 131 | read_fw FW_CFG_INITRD_SIZE |
| 132 | subl %eax, %edi |
| 133 | andl $-4096, %edi /* EDI = start of initrd */ |
Paolo Bonzini | 269e235 | 2014-12-11 02:17:03 +0100 | [diff] [blame] | 134 | movl %edi, %es:0x218 /* put it in the header */ |
Alexander Graf | 57a46d0 | 2009-11-12 21:53:14 +0100 | [diff] [blame] | 135 | |
Paolo Bonzini | 269e235 | 2014-12-11 02:17:03 +0100 | [diff] [blame] | 136 | load_kernel: |
Alexander Graf | 57a46d0 | 2009-11-12 21:53:14 +0100 | [diff] [blame] | 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 | |
Paolo Bonzini | 36ecd7c | 2009-12-13 11:36:40 +0100 | [diff] [blame] | 141 | /* Reserve space on the stack for our GDT descriptor. */ |
Paolo Bonzini | cdebec5 | 2014-10-06 16:49:57 +0200 | [diff] [blame] | 142 | mov %esp, %ebp |
| 143 | sub $16, %esp |
Alexander Graf | 57a46d0 | 2009-11-12 21:53:14 +0100 | [diff] [blame] | 144 | |
| 145 | /* Now create the GDT descriptor */ |
Paolo Bonzini | 36ecd7c | 2009-12-13 11:36:40 +0100 | [diff] [blame] | 146 | movw $((3 * 8) - 1), -16(%bp) |
Alexander Graf | 57a46d0 | 2009-11-12 21:53:14 +0100 | [diff] [blame] | 147 | mov %cs, %eax |
Avi Kivity | d0652aa | 2009-12-24 15:38:50 +0200 | [diff] [blame] | 148 | movzwl %ax, %eax |
Alexander Graf | 57a46d0 | 2009-11-12 21:53:14 +0100 | [diff] [blame] | 149 | shl $4, %eax |
Avi Kivity | d0652aa | 2009-12-24 15:38:50 +0200 | [diff] [blame] | 150 | addl $gdt, %eax |
| 151 | movl %eax, -14(%bp) |
Alexander Graf | 57a46d0 | 2009-11-12 21:53:14 +0100 | [diff] [blame] | 152 | |
| 153 | /* And load the GDT */ |
Paolo Bonzini | 36ecd7c | 2009-12-13 11:36:40 +0100 | [diff] [blame] | 154 | data32 lgdt -16(%bp) |
| 155 | mov %ebp, %esp |
Alexander Graf | 57a46d0 | 2009-11-12 21:53:14 +0100 | [diff] [blame] | 156 | |
| 157 | /* Get us to protected mode now */ |
| 158 | mov $1, %eax |
| 159 | mov %eax, %cr0 |
| 160 | |
Alexander Graf | dc61b0d | 2009-11-17 17:49:16 +0100 | [diff] [blame] | 161 | /* So we can set ES to a 32-bit segment */ |
Alexander Graf | 57a46d0 | 2009-11-12 21:53:14 +0100 | [diff] [blame] | 162 | mov $0x10, %eax |
Alexander Graf | dc61b0d | 2009-11-17 17:49:16 +0100 | [diff] [blame] | 163 | mov %eax, %es |
Alexander Graf | 57a46d0 | 2009-11-12 21:53:14 +0100 | [diff] [blame] | 164 | |
Alexander Graf | dc61b0d | 2009-11-17 17:49:16 +0100 | [diff] [blame] | 165 | /* We're now running in 16-bit CS, but 32-bit ES! */ |
Alexander Graf | 57a46d0 | 2009-11-12 21:53:14 +0100 | [diff] [blame] | 166 | |
| 167 | /* Load kernel and initrd */ |
Paolo Bonzini | cdebec5 | 2014-10-06 16:49:57 +0200 | [diff] [blame] | 168 | read_fw_blob_addr32_edi(FW_CFG_INITRD) |
Alexander Graf | 590bf49 | 2010-06-02 01:56:50 +0200 | [diff] [blame] | 169 | read_fw_blob_addr32(FW_CFG_KERNEL) |
Alexander Graf | 590bf49 | 2010-06-02 01:56:50 +0200 | [diff] [blame] | 170 | read_fw_blob_addr32(FW_CFG_CMDLINE) |
Paolo Bonzini | cdebec5 | 2014-10-06 16:49:57 +0200 | [diff] [blame] | 171 | |
Alexander Graf | 57a46d0 | 2009-11-12 21:53:14 +0100 | [diff] [blame] | 172 | /* And now jump into Linux! */ |
| 173 | mov $0, %eax |
| 174 | mov %eax, %cr0 |
| 175 | |
Alexander Graf | dc61b0d | 2009-11-17 17:49:16 +0100 | [diff] [blame] | 176 | /* ES = CS */ |
Alexander Graf | 57a46d0 | 2009-11-12 21:53:14 +0100 | [diff] [blame] | 177 | mov %cs, %ax |
Alexander Graf | dc61b0d | 2009-11-17 17:49:16 +0100 | [diff] [blame] | 178 | mov %ax, %es |
Alexander Graf | 57a46d0 | 2009-11-12 21:53:14 +0100 | [diff] [blame] | 179 | |
| 180 | jmp boot_kernel |
| 181 | |
| 182 | /* Variables */ |
| 183 | |
| 184 | .align 4, 0 |
| 185 | gdt: |
| 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 | |
| 195 | BOOT_ROM_END |