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 /* Compute initrd address */ 80 mov $0xe801, %ax 81 xor %cx, %cx 82 xor %dx, %dx 83 int $0x15 84 85 /* Output could be in AX/BX or CX/DX */ 86 or %cx, %cx 87 jnz 1f 88 or %dx, %dx 89 jnz 1f 90 mov %ax, %cx 91 mov %bx, %dx 921: 93 94 or %dx, %dx 95 jnz 2f 96 addw $1024, %cx /* add 1 MB */ 97 movzwl %cx, %edi 98 shll $10, %edi /* convert to bytes */ 99 jmp 3f 100 1012: 102 addw $16777216 >> 16, %dx /* add 16 MB */ 103 movzwl %dx, %edi 104 shll $16, %edi /* convert to bytes */ 105 1063: 107 read_fw FW_CFG_INITRD_SIZE 108 subl %eax, %edi 109 andl $-4096, %edi /* EDI = start of initrd */ 110 111 /* We need to load the kernel into memory we can't access in 16 bit 112 mode, so let's get into 32 bit mode, write the kernel and jump 113 back again. */ 114 115 /* Reserve space on the stack for our GDT descriptor. */ 116 mov %esp, %ebp 117 sub $16, %esp 118 119 /* Now create the GDT descriptor */ 120 movw $((3 * 8) - 1), -16(%bp) 121 mov %cs, %eax 122 movzwl %ax, %eax 123 shl $4, %eax 124 addl $gdt, %eax 125 movl %eax, -14(%bp) 126 127 /* And load the GDT */ 128 data32 lgdt -16(%bp) 129 mov %ebp, %esp 130 131 /* Get us to protected mode now */ 132 mov $1, %eax 133 mov %eax, %cr0 134 135 /* So we can set ES to a 32-bit segment */ 136 mov $0x10, %eax 137 mov %eax, %es 138 139 /* We're now running in 16-bit CS, but 32-bit ES! */ 140 141 /* Load kernel and initrd */ 142 pushl %edi 143 read_fw_blob_addr32_edi(FW_CFG_INITRD) 144 read_fw_blob_addr32(FW_CFG_KERNEL) 145 read_fw_blob_addr32(FW_CFG_CMDLINE) 146 147 read_fw FW_CFG_SETUP_ADDR 148 mov %eax, %edi 149 mov %eax, %ebx 150 read_fw_blob_addr32_edi(FW_CFG_SETUP) 151 152 /* Update the header with the initrd address we chose above */ 153 popl %es:0x218(%ebx) 154 155 /* And now jump into Linux! */ 156 mov $0, %eax 157 mov %eax, %cr0 158 159 /* ES = CS */ 160 mov %cs, %ax 161 mov %ax, %es 162 163 jmp boot_kernel 164 165/* Variables */ 166 167.align 4, 0 168gdt: 169 /* 0x00 */ 170.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 171 172 /* 0x08: code segment (base=0, limit=0xfffff, type=32bit code exec/read, DPL=0, 4k) */ 173.byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00 174 175 /* 0x10: data segment (base=0, limit=0xfffff, type=32bit data read/write, DPL=0, 4k) */ 176.byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0xcf, 0x00 177 178BOOT_ROM_END 179