157a46d05SAlexander Graf/* 257a46d05SAlexander Graf * Linux Boot Option ROM 357a46d05SAlexander Graf * 457a46d05SAlexander Graf * This program is free software; you can redistribute it and/or modify 557a46d05SAlexander Graf * it under the terms of the GNU General Public License as published by 657a46d05SAlexander Graf * the Free Software Foundation; either version 2 of the License, or 757a46d05SAlexander Graf * (at your option) any later version. 857a46d05SAlexander Graf * 957a46d05SAlexander Graf * This program is distributed in the hope that it will be useful, 1057a46d05SAlexander Graf * but WITHOUT ANY WARRANTY; without even the implied warranty of 1157a46d05SAlexander Graf * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1257a46d05SAlexander Graf * GNU General Public License for more details. 1357a46d05SAlexander Graf * 1457a46d05SAlexander Graf * You should have received a copy of the GNU General Public License 1557a46d05SAlexander Graf * along with this program; if not, see <http://www.gnu.org/licenses/>. 1657a46d05SAlexander Graf * 1757a46d05SAlexander Graf * Copyright Novell Inc, 2009 1857a46d05SAlexander Graf * Authors: Alexander Graf <agraf@suse.de> 1957a46d05SAlexander Graf * 2057a46d05SAlexander Graf * Based on code in hw/pc.c. 2157a46d05SAlexander Graf */ 2257a46d05SAlexander Graf 2357a46d05SAlexander Graf#include "optionrom.h" 2457a46d05SAlexander Graf 2575b9f690SGleb Natapov#define BOOT_ROM_PRODUCT "Linux loader" 2675b9f690SGleb Natapov 2757a46d05SAlexander GrafBOOT_ROM_START 2857a46d05SAlexander Graf 2957a46d05SAlexander Grafrun_linuxboot: 3057a46d05SAlexander Graf 3157a46d05SAlexander Graf cli 3257a46d05SAlexander Graf cld 3357a46d05SAlexander Graf 3457a46d05SAlexander Graf jmp copy_kernel 3557a46d05SAlexander Grafboot_kernel: 3657a46d05SAlexander Graf 3757a46d05SAlexander Graf read_fw FW_CFG_SETUP_ADDR 3857a46d05SAlexander Graf 3957a46d05SAlexander Graf mov %eax, %ebx 4057a46d05SAlexander Graf shr $4, %ebx 4157a46d05SAlexander Graf 4257a46d05SAlexander Graf /* All segments contain real_addr */ 4357a46d05SAlexander Graf mov %bx, %ds 4457a46d05SAlexander Graf mov %bx, %es 4557a46d05SAlexander Graf mov %bx, %fs 4657a46d05SAlexander Graf mov %bx, %gs 4757a46d05SAlexander Graf mov %bx, %ss 4857a46d05SAlexander Graf 4957a46d05SAlexander Graf /* CX = CS we want to jump to */ 5057a46d05SAlexander Graf add $0x20, %bx 5157a46d05SAlexander Graf mov %bx, %cx 5257a46d05SAlexander Graf 5357a46d05SAlexander Graf /* SP = cmdline_addr-real_addr-16 */ 5457a46d05SAlexander Graf read_fw FW_CFG_CMDLINE_ADDR 5557a46d05SAlexander Graf mov %eax, %ebx 5657a46d05SAlexander Graf read_fw FW_CFG_SETUP_ADDR 5757a46d05SAlexander Graf sub %eax, %ebx 5857a46d05SAlexander Graf sub $16, %ebx 5957a46d05SAlexander Graf mov %ebx, %esp 6057a46d05SAlexander Graf 6157a46d05SAlexander Graf /* Build indirect lret descriptor */ 6257a46d05SAlexander Graf pushw %cx /* CS */ 6357a46d05SAlexander Graf xor %ax, %ax 6457a46d05SAlexander Graf pushw %ax /* IP = 0 */ 6557a46d05SAlexander Graf 6657a46d05SAlexander Graf /* Clear registers */ 6757a46d05SAlexander Graf xor %eax, %eax 6857a46d05SAlexander Graf xor %ebx, %ebx 6957a46d05SAlexander Graf xor %ecx, %ecx 7057a46d05SAlexander Graf xor %edx, %edx 7157a46d05SAlexander Graf xor %edi, %edi 7257a46d05SAlexander Graf xor %ebp, %ebp 7357a46d05SAlexander Graf 7457a46d05SAlexander Graf /* Jump to Linux */ 7557a46d05SAlexander Graf lret 7657a46d05SAlexander Graf 7757a46d05SAlexander Graf 7857a46d05SAlexander Grafcopy_kernel: 79*269e2358SPaolo Bonzini /* Read info block in low memory (0x10000 or 0x90000) */ 80*269e2358SPaolo Bonzini read_fw FW_CFG_SETUP_ADDR 81*269e2358SPaolo Bonzini shr $4, %eax 82*269e2358SPaolo Bonzini mov %eax, %es 83*269e2358SPaolo Bonzini xor %edi, %edi 84*269e2358SPaolo Bonzini read_fw_blob_addr32_edi(FW_CFG_SETUP) 85*269e2358SPaolo Bonzini 86*269e2358SPaolo Bonzini cmpw $0x203, %es:0x206 // if protocol >= 0x203 87*269e2358SPaolo Bonzini jae 1f // have initrd_max 88*269e2358SPaolo Bonzini movl $0x37ffffff, %es:0x22c // else assume 0x37ffffff 89*269e2358SPaolo Bonzini1: 90*269e2358SPaolo Bonzini 91*269e2358SPaolo Bonzini /* Check if using kernel-specified initrd address */ 92*269e2358SPaolo Bonzini read_fw FW_CFG_INITRD_ADDR 93*269e2358SPaolo Bonzini mov %eax, %edi // (load_kernel wants it in %edi) 94*269e2358SPaolo Bonzini read_fw FW_CFG_INITRD_SIZE // find end of initrd 95*269e2358SPaolo Bonzini add %edi, %eax 96*269e2358SPaolo Bonzini xor %es:0x22c, %eax // if it matches es:0x22c 97*269e2358SPaolo Bonzini and $-4096, %eax // (apart from padding for page) 98*269e2358SPaolo Bonzini jz load_kernel // then initrd is not at top 99*269e2358SPaolo Bonzini // of memory 100*269e2358SPaolo Bonzini 101*269e2358SPaolo Bonzini /* pc.c placed the initrd at end of memory. Compute a better 102*269e2358SPaolo Bonzini * initrd address based on e801 data. 103*269e2358SPaolo Bonzini */ 104cdebec5eSPaolo Bonzini mov $0xe801, %ax 105cdebec5eSPaolo Bonzini xor %cx, %cx 106cdebec5eSPaolo Bonzini xor %dx, %dx 107cdebec5eSPaolo Bonzini int $0x15 108cdebec5eSPaolo Bonzini 109cdebec5eSPaolo Bonzini /* Output could be in AX/BX or CX/DX */ 110cdebec5eSPaolo Bonzini or %cx, %cx 111cdebec5eSPaolo Bonzini jnz 1f 112cdebec5eSPaolo Bonzini or %dx, %dx 113cdebec5eSPaolo Bonzini jnz 1f 114cdebec5eSPaolo Bonzini mov %ax, %cx 115cdebec5eSPaolo Bonzini mov %bx, %dx 116cdebec5eSPaolo Bonzini1: 117cdebec5eSPaolo Bonzini 118cdebec5eSPaolo Bonzini or %dx, %dx 119cdebec5eSPaolo Bonzini jnz 2f 120cdebec5eSPaolo Bonzini addw $1024, %cx /* add 1 MB */ 121cdebec5eSPaolo Bonzini movzwl %cx, %edi 122cdebec5eSPaolo Bonzini shll $10, %edi /* convert to bytes */ 123cdebec5eSPaolo Bonzini jmp 3f 124cdebec5eSPaolo Bonzini 125cdebec5eSPaolo Bonzini2: 126cdebec5eSPaolo Bonzini addw $16777216 >> 16, %dx /* add 16 MB */ 127cdebec5eSPaolo Bonzini movzwl %dx, %edi 128cdebec5eSPaolo Bonzini shll $16, %edi /* convert to bytes */ 129cdebec5eSPaolo Bonzini 130cdebec5eSPaolo Bonzini3: 131cdebec5eSPaolo Bonzini read_fw FW_CFG_INITRD_SIZE 132cdebec5eSPaolo Bonzini subl %eax, %edi 133cdebec5eSPaolo Bonzini andl $-4096, %edi /* EDI = start of initrd */ 134*269e2358SPaolo Bonzini movl %edi, %es:0x218 /* put it in the header */ 13557a46d05SAlexander Graf 136*269e2358SPaolo Bonziniload_kernel: 13757a46d05SAlexander Graf /* We need to load the kernel into memory we can't access in 16 bit 13857a46d05SAlexander Graf mode, so let's get into 32 bit mode, write the kernel and jump 13957a46d05SAlexander Graf back again. */ 14057a46d05SAlexander Graf 14136ecd7c0SPaolo Bonzini /* Reserve space on the stack for our GDT descriptor. */ 14236ecd7c0SPaolo Bonzini mov %esp, %ebp 14336ecd7c0SPaolo Bonzini sub $16, %esp 14457a46d05SAlexander Graf 14557a46d05SAlexander Graf /* Now create the GDT descriptor */ 14636ecd7c0SPaolo Bonzini movw $((3 * 8) - 1), -16(%bp) 14757a46d05SAlexander Graf mov %cs, %eax 148d0652aa8SAvi Kivity movzwl %ax, %eax 14957a46d05SAlexander Graf shl $4, %eax 150d0652aa8SAvi Kivity addl $gdt, %eax 151d0652aa8SAvi Kivity movl %eax, -14(%bp) 15257a46d05SAlexander Graf 15357a46d05SAlexander Graf /* And load the GDT */ 15436ecd7c0SPaolo Bonzini data32 lgdt -16(%bp) 15536ecd7c0SPaolo Bonzini mov %ebp, %esp 15657a46d05SAlexander Graf 15757a46d05SAlexander Graf /* Get us to protected mode now */ 15857a46d05SAlexander Graf mov $1, %eax 15957a46d05SAlexander Graf mov %eax, %cr0 16057a46d05SAlexander Graf 161dc61b0dcSAlexander Graf /* So we can set ES to a 32-bit segment */ 16257a46d05SAlexander Graf mov $0x10, %eax 163dc61b0dcSAlexander Graf mov %eax, %es 16457a46d05SAlexander Graf 165dc61b0dcSAlexander Graf /* We're now running in 16-bit CS, but 32-bit ES! */ 16657a46d05SAlexander Graf 16757a46d05SAlexander Graf /* Load kernel and initrd */ 168cdebec5eSPaolo Bonzini read_fw_blob_addr32_edi(FW_CFG_INITRD) 169590bf491SAlexander Graf read_fw_blob_addr32(FW_CFG_KERNEL) 170590bf491SAlexander Graf read_fw_blob_addr32(FW_CFG_CMDLINE) 171cdebec5eSPaolo Bonzini 17257a46d05SAlexander Graf /* And now jump into Linux! */ 17357a46d05SAlexander Graf mov $0, %eax 17457a46d05SAlexander Graf mov %eax, %cr0 17557a46d05SAlexander Graf 176dc61b0dcSAlexander Graf /* ES = CS */ 17757a46d05SAlexander Graf mov %cs, %ax 178dc61b0dcSAlexander Graf mov %ax, %es 17957a46d05SAlexander Graf 18057a46d05SAlexander Graf jmp boot_kernel 18157a46d05SAlexander Graf 18257a46d05SAlexander Graf/* Variables */ 18357a46d05SAlexander Graf 18457a46d05SAlexander Graf.align 4, 0 18557a46d05SAlexander Grafgdt: 18657a46d05SAlexander Graf /* 0x00 */ 18757a46d05SAlexander Graf.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 18857a46d05SAlexander Graf 18957a46d05SAlexander Graf /* 0x08: code segment (base=0, limit=0xfffff, type=32bit code exec/read, DPL=0, 4k) */ 19057a46d05SAlexander Graf.byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00 19157a46d05SAlexander Graf 19257a46d05SAlexander Graf /* 0x10: data segment (base=0, limit=0xfffff, type=32bit data read/write, DPL=0, 4k) */ 19357a46d05SAlexander Graf.byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0xcf, 0x00 19457a46d05SAlexander Graf 19557a46d05SAlexander GrafBOOT_ROM_END 196