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