Add qemu 2.4.0
[kvmfornfv.git] / qemu / pc-bios / optionrom / multiboot.S
1 /*
2  * Multiboot 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
21 #include "optionrom.h"
22
23 #define BOOT_ROM_PRODUCT "multiboot loader"
24
25 #define MULTIBOOT_MAGIC         0x2badb002
26
27 #define GS_PROT_JUMP            0
28 #define GS_GDT_DESC             6
29
30
31 BOOT_ROM_START
32
33 run_multiboot:
34
35         cli
36         cld
37
38         mov             %cs, %eax
39         shl             $0x4, %eax
40
41         /* set up a long jump descriptor that is PC relative */
42
43         /* move stack memory to %gs */
44         mov             %ss, %ecx
45         shl             $0x4, %ecx
46         mov             %esp, %ebx
47         add             %ebx, %ecx
48         sub             $0x20, %ecx
49         sub             $0x30, %esp
50         shr             $0x4, %ecx
51         mov             %cx, %gs
52
53         /* now push the indirect jump descriptor there */
54         mov             (prot_jump), %ebx
55         add             %eax, %ebx
56         movl            %ebx, %gs:GS_PROT_JUMP
57         mov             $8, %bx
58         movw            %bx, %gs:GS_PROT_JUMP + 4
59
60         /* fix the gdt descriptor to be PC relative */
61         movw            (gdt_desc), %bx
62         movw            %bx, %gs:GS_GDT_DESC
63         movl            (gdt_desc+2), %ebx
64         add             %eax, %ebx
65         movl            %ebx, %gs:GS_GDT_DESC + 2
66
67         xor             %eax, %eax
68         mov             %eax, %es
69
70         /* Read the bootinfo struct into RAM */
71         read_fw_blob(FW_CFG_INITRD)
72
73         /* FS = bootinfo_struct */
74         read_fw         FW_CFG_INITRD_ADDR
75         shr             $4, %eax
76         mov             %ax, %fs
77
78         /* Account for the EBDA in the multiboot structure's e801
79          * map.
80          */
81         int             $0x12
82         cwtl
83         movl            %eax, %fs:4
84
85         /* ES = mmap_addr */
86         mov             %fs:48, %eax
87         shr             $4, %eax
88         mov             %ax, %es
89
90         /* Initialize multiboot mmap structs using int 0x15(e820) */
91         xor             %ebx, %ebx
92         /* Start storing mmap data at %es:0 */
93         xor             %edi, %edi
94
95 mmap_loop:
96         /* The multiboot entry size has offset -4, so leave some space */
97         add             $4, %di
98         /* entry size (mmap struct) & max buffer size (int15) */
99         movl            $20, %ecx
100         /* e820 */
101         movl            $0x0000e820, %eax
102         /* 'SMAP' magic */
103         movl            $0x534d4150, %edx
104         int             $0x15
105
106 mmap_check_entry:
107         /* Error or last entry already done? */
108         jb              mmap_done
109
110 mmap_store_entry:
111         /* store entry size */
112         /* old as(1) doesn't like this insn so emit the bytes instead:
113         movl            %ecx, %es:-4(%edi)
114         */
115         .dc.b           0x26,0x67,0x66,0x89,0x4f,0xfc
116
117         /* %edi += entry_size, store as mbs_mmap_length */
118         add             %ecx, %edi
119         movw            %di, %fs:0x2c
120
121         /* Continuation value 0 means last entry */
122         test            %ebx, %ebx
123         jnz             mmap_loop
124
125 mmap_done:
126         /* Calculate upper_mem field: The amount of memory between 1 MB and
127            the first upper memory hole. Get it from the mmap. */
128         xor             %di, %di
129         mov             $0x100000, %edx
130 upper_mem_entry:
131         cmp             %fs:0x2c, %di
132         je              upper_mem_done
133         add             $4, %di
134
135         /* Skip if type != 1 */
136         cmpl            $1, %es:16(%di)
137         jne             upper_mem_next
138
139         /* Skip if > 4 GB */
140         movl            %es:4(%di), %eax
141         test            %eax, %eax
142         jnz             upper_mem_next
143
144         /* Check for contiguous extension (base <= %edx < base + length) */
145         movl            %es:(%di), %eax
146         cmp             %eax, %edx
147         jb              upper_mem_next
148         addl            %es:8(%di), %eax
149         cmp             %eax, %edx
150         jae             upper_mem_next
151
152         /* If so, update %edx, and restart the search (mmap isn't ordered) */
153         mov             %eax, %edx
154         xor             %di, %di
155         jmp             upper_mem_entry
156
157 upper_mem_next:
158         addl            %es:-4(%di), %edi
159         jmp             upper_mem_entry
160
161 upper_mem_done:
162         sub             $0x100000, %edx
163         shr             $10, %edx
164         mov             %edx, %fs:0x8
165
166 real_to_prot:
167         /* Load the GDT before going into protected mode */
168 lgdt:
169         data32 lgdt     %gs:GS_GDT_DESC
170
171         /* get us to protected mode now */
172         movl            $1, %eax
173         movl            %eax, %cr0
174
175         /* the LJMP sets CS for us and gets us to 32-bit */
176 ljmp:
177         data32 ljmp     *%gs:GS_PROT_JUMP
178
179 prot_mode:
180 .code32
181
182         /* initialize all other segments */
183         movl            $0x10, %eax
184         movl            %eax, %ss
185         movl            %eax, %ds
186         movl            %eax, %es
187         movl            %eax, %fs
188         movl            %eax, %gs
189
190         /* Read the kernel and modules into RAM */
191         read_fw_blob(FW_CFG_KERNEL)
192
193         /* Jump off to the kernel */
194         read_fw         FW_CFG_KERNEL_ENTRY
195         mov             %eax, %ecx
196
197         /* EBX contains a pointer to the bootinfo struct */
198         read_fw         FW_CFG_INITRD_ADDR
199         movl            %eax, %ebx
200
201         /* EAX has to contain the magic */
202         movl            $MULTIBOOT_MAGIC, %eax
203 ljmp2:
204         jmp             *%ecx
205
206 /* Variables */
207 .align 4, 0
208 prot_jump:      .long prot_mode
209                 .short 8
210
211 .align 4, 0
212 gdt:
213         /* 0x00 */
214 .byte   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
215
216         /* 0x08: code segment (base=0, limit=0xfffff, type=32bit code exec/read, DPL=0, 4k) */
217 .byte   0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00
218
219         /* 0x10: data segment (base=0, limit=0xfffff, type=32bit data read/write, DPL=0, 4k) */
220 .byte   0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0xcf, 0x00
221
222         /* 0x18: code segment (base=0, limit=0x0ffff, type=16bit code exec/read/conf, DPL=0, 1b) */
223 .byte   0xff, 0xff, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00
224
225         /* 0x20: data segment (base=0, limit=0x0ffff, type=16bit data read/write, DPL=0, 1b) */
226 .byte   0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00
227
228 gdt_desc:
229 .short  (5 * 8) - 1
230 .long   gdt
231
232 BOOT_ROM_END