xref: /openbmc/qemu/pc-bios/optionrom/linuxboot.S (revision dfa9c2a0f4d0a0c8b2c1449ecdbb1297427e1560)
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