Add qemu 2.4.0
[kvmfornfv.git] / qemu / pc-bios / optionrom / linuxboot.S
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
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:
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          */
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 */
134         movl            %edi, %es:0x218       /* put it in the header */
135
136 load_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
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