Adjust memory layout for 2.6.22+ kernels with 32KB setup code
[mknbi.git] / start32.S
1 /* #defines because ljmp wants a number, probably gas bug */
2 /*      .equ    KERN_CODE_SEG,_pmcs-_gdt        */
3 #define KERN_CODE_SEG   0x08
4         .equ    KERN_DATA_SEG,_pmds-_gdt
5 /*      .equ    REAL_CODE_SEG,_rmcs-_gdt        */
6 #define REAL_CODE_SEG   0x18
7         .equ    REAL_DATA_SEG,_rmds-_gdt
8         .equ    CR0_PE,1
9
10 #ifdef  GAS291
11 #define DATA32 data32;
12 #define ADDR32 addr32;
13 #define LJMPI(x)        ljmp    x
14 #else
15 #define DATA32 data32
16 #define ADDR32 addr32
17 /* newer GAS295 require #define LJMPI(x)        ljmp    *x */
18 #define LJMPI(x)        ljmp    x
19 #endif
20
21 /* Size of segment allocated to first32.c in protected mode */
22 #define FIRST32SIZE     (6*1024)
23
24 /*
25  * NOTE: if you write a subroutine that is called from C code (gcc/egcs),
26  * then you only have to take care of %ebx, %esi, %edi and %ebp.  These
27  * registers must not be altered under any circumstance.  All other registers
28  * may be clobbered without any negative side effects.  If you don't follow
29  * this rule then you'll run into strange effects that only occur on some
30  * gcc versions (because the register allocator may use different registers).
31  *
32  * All the data32 prefixes for the ljmp instructions are necessary, because
33  * the assembler emits code with a relocation address of 0.  This means that
34  * all destinations are initially negative, which the assembler doesn't grok,
35  * because for some reason negative numbers don't fit into 16 bits. The addr32
36  * prefixes are there for the same reasons, because otherwise the memory
37  * references are only 16 bit wide.  Theoretically they are all superfluous.
38  * One last note about prefixes: the data32 prefixes on all call _real_to_prot
39  * instructions could be removed if the _real_to_prot function is changed to
40  * deal correctly with 16 bit return addresses.  I tried it, but failed.
41  */
42
43 /**************************************************************************
44 START - Where all the fun begins....
45 **************************************************************************/
46 /* this must be the first thing in the file because we enter from the top */
47         .global _start
48 _start:
49 /* We have to use our own GDT when running in our segment because the old
50    GDT will have the wrong descriptors for the real code segments */
51         sgdt    gdtsave         /* save old GDT */
52         lgdt    gdtarg          /* load ours */
53         /* reload the segment registers */
54         movl    $KERN_DATA_SEG,%eax
55         movl    %eax,%ds
56         movl    %eax,%es
57         movl    %eax,%ss
58         movl    %eax,%fs
59         movl    %eax,%gs
60         /* flush prefetch queue, and reload %cs:%eip */
61         ljmp    $KERN_CODE_SEG,$1f
62 1:
63         /* save the stack pointer and jump to the routine */
64         movl    %esp,%eax
65         movl    %eax,initsp
66         movl    $RELOC+FIRST32SIZE,%esp /* change stack */
67         /* copy the arguments on the stack over */
68         pushl   12(%eax)                /* bootp */
69         pushl   8(%eax)                 /* header */
70         pushl   4(%eax)                 /* ebinfo */
71         call    first
72         /* fall through */
73
74 _exit:
75 /*      we reset sp to the location just before entering first
76         instead of relying on the return from first because exit
77         could have been called from anywhere */
78         movl    initsp,%ebx
79         movl    %ebx,%esp
80         lgdt    gdtsave         /* restore old GDT */
81         ret
82
83         .globl  exit
84 exit:   movl    4(%esp),%eax
85         jmp     _exit
86
87 /**************************************************************************
88 CONSOLE_PUTC - Print a character on console
89 **************************************************************************/
90         .globl  console_putc
91 console_putc:
92         pushl   %ebp
93         movl    %esp,%ebp
94         pushl   %ebx
95         pushl   %esi
96         pushl   %edi
97         movb    8(%ebp),%cl
98         call    _prot_to_real
99         .code16
100         movl    $1,%ebx
101         movb    $0x0e,%ah
102         movb    %cl,%al
103         int     $0x10
104         DATA32 call     _real_to_prot
105         .code32
106         popl    %edi
107         popl    %esi
108         popl    %ebx
109         popl    %ebp
110         ret
111
112 /**************************************************************************
113 E820_MEMSIZE - Get a listing of memory regions
114 **************************************************************************/
115         .globl  meme820
116 #define SMAP    0x534d4150
117 meme820:
118         pushl   %ebp
119         movl    %esp, %ebp
120         pushl   %ebx
121         pushl   %esi
122         pushl   %edi
123         movl    8(%ebp), %edi   /* Address to return e820 structures at */
124         subl    $RELOC, %edi
125         movl    12(%ebp), %esi  /* Maximum number of e820 structurs to return */
126         pushl   %esi
127         call    _prot_to_real
128         .code16
129         xorl    %ebx, %ebx
130 jmpe820:        
131         movl    $0xe820, %eax
132         movl    $SMAP, %edx
133         movl    $20, %ecx
134         /* %di was setup earlier */
135         int     $0x15
136         jc      bail820
137
138         cmpl    $SMAP, %eax
139         jne     bail820
140
141 good820:        
142         /* If this is useable memory, we save it by simply advancing %di by
143          * sizeof(e820rec)
144          */
145         decl    %esi
146         testl   %esi,%esi
147         jz      bail820
148
149         addw    $20, %di
150 again820:
151         cmpl    $0, %ebx        /* check to see if %ebx is set to EOF */
152         jne     jmpe820
153
154 bail820:
155         DATA32 call     _real_to_prot
156         .code32
157         popl    %eax
158         subl    %esi, %eax      /* Compute how many structure we read */
159
160         /* Restore everything else */   
161         popl    %edi
162         popl    %esi
163         popl    %ebx
164         movl    %ebp, %esp
165         popl    %ebp
166         ret
167
168 /**************************************************************************
169 MEMSIZE - Determine size of extended memory
170 **************************************************************************/
171         .globl  memsize
172 memsize:
173         pushl   %ebx
174         pushl   %esi
175         pushl   %edi
176         call    _prot_to_real
177         .code16
178         stc                                     # fix to work around buggy
179         xorw    %cx,%cx                         # BIOSes which dont clear/set
180         xorw    %dx,%dx                         # carry on pass/error of
181                                                 # e801h memory size call
182                                                 # or merely pass cx,dx though
183                                                 # without changing them.
184         movw    $0xe801,%ax
185         int     $0x15
186         jc      3f
187         cmpw    $0,%cx                          # Kludge to handle BIOSes
188         jne     1f                              # which report their extended
189         cmpw    $0,%dx                          # memory in AX/BX rather than
190         je      2f                              # CX/DX.  The spec I have read
191 1:
192         movw    %cx,%ax                         # seems to indicate AX/BX 
193         movw    %dx,%bx                         # are more reasonable anyway...
194 2:
195         andl    $0xffff,%eax
196         andl    $0xffff,%ebx
197         shll    $6,%ebx
198         addl    %ebx,%eax
199         jmp     4f
200 3:
201         movw    $0x8800,%ax
202         int     $0x15
203         andl    $0xffff,%eax
204 4:
205         movl    %eax,%esi
206         DATA32 call     _real_to_prot
207         .code32
208         movl    %esi,%eax
209         popl    %edi
210         popl    %esi
211         popl    %ebx
212         ret
213
214 /**************************************************************************
215 BASEMEMSIZE - Get size of the conventional (base) memory
216 **************************************************************************/
217         .globl  basememsize
218 basememsize:
219         call    _prot_to_real
220         .code16
221         int     $0x12
222         movw    %ax,%cx
223         DATA32 call     _real_to_prot
224         .code32
225         movw    %cx,%ax
226         ret
227
228 /**************************************************************************
229 XSTARTLINUX - Transfer control to the kernel just loaded in real mode
230 **************************************************************************/
231         .globl  xstartlinux
232 xstartlinux:
233         pushl   %ebp
234         movl    %esp,%ebp
235         pushl   %ebx
236         pushl   %esi
237         pushl   %edi
238         movl    8(%ebp),%eax
239 /*      Convert to segment:offset form */
240         movl    %eax,%ebx
241         andl    $0xF,%eax
242         andl    $0xFFFFFFF0,%ebx
243         shll    $12,%ebx
244         orl     %eax,%ebx
245         call    _prot_to_real
246         .code16
247 /* (isac) some kernels expect segment registers to point to start of real-mode
248  * code (see Documentation/i386/boot.txt:RUNNING THE KERNEL)
249  */
250         movl    %ebx,%eax
251         shrl    $16,%eax
252         subw    $0x20,%ax /* RM start is seg. offset 0x20 before entry point */
253         movw    %ax,%ds
254         movw    %ax,%es
255         movw    %ax,%fs
256         movw    %ax,%gs
257         movl    $((RELOC<<12)+(1f-RELOC)),%eax
258         pushl   %eax
259         pushl   %ebx
260         lret
261 1:
262         addw    $4,%sp          /* XXX or is this 10 in case of a 16bit "ret" */
263         DATA32 call     _real_to_prot
264         .code32
265         popl    %edi
266         popl    %esi
267         popl    %ebx
268         popl    %ebp
269         ret
270
271 /**************************************************************************
272 _REAL_TO_PROT - Go from REAL mode to Protected Mode
273 **************************************************************************/
274         .globl  _real_to_prot
275 _real_to_prot:
276         .code16
277         cli
278         cs
279         ADDR32 lgdt     gdtarg-_start
280         movl    %cr0,%eax
281         orl     $CR0_PE,%eax
282         movl    %eax,%cr0               /* turn on protected mode */
283
284         /* flush prefetch queue, and reload %cs:%eip */
285         DATA32 ljmp     $KERN_CODE_SEG,$1f
286 1:
287         .code32
288         /* reload other segment registers */
289         movl    $KERN_DATA_SEG,%eax
290         movl    %eax,%ds
291         movl    %eax,%es
292         movl    %eax,%ss
293         addl    $RELOC,%esp             /* Fix up stack pointer */
294         xorl    %eax,%eax
295         movl    %eax,%fs
296         movl    %eax,%gs
297         popl    %eax                    /* Fix up return address */
298         addl    $RELOC,%eax
299         pushl   %eax
300         ret
301
302 /**************************************************************************
303 _PROT_TO_REAL - Go from Protected Mode to REAL Mode
304 **************************************************************************/
305         .globl  _prot_to_real
306 _prot_to_real:
307         .code32
308         popl    %eax
309         subl    $RELOC,%eax             /* Adjust return address */
310         pushl   %eax
311         subl    $RELOC,%esp             /* Adjust stack pointer */
312 #ifdef  GAS291
313         ljmp    $REAL_CODE_SEG,$1f-RELOC        /* jump to a 16 bit segment */
314 #else
315         ljmp    $REAL_CODE_SEG,$1f-_start       /* jump to a 16 bit segment */
316 #endif  /* GAS291 */
317 1:
318         .code16
319         movw    $REAL_DATA_SEG,%ax
320         movw    %ax,%ds
321         movw    %ax,%ss
322         movw    %ax,%es
323         movw    %ax,%fs
324         movw    %ax,%gs
325
326         /* clear the PE bit of CR0 */
327         movl    %cr0,%eax
328         andl    $0!CR0_PE,%eax
329         movl    %eax,%cr0
330
331         /* make intersegment jmp to flush the processor pipeline
332          * and reload %cs:%eip (to clear upper 16 bits of %eip).
333          */
334         DATA32 ljmp     $(RELOC)>>4,$2f-_start
335 2:
336         /* we are in real mode now
337          * set up the real mode segment registers : %ds, $ss, %es
338          */
339         movw    %cs,%ax
340         movw    %ax,%ds
341         movw    %ax,%es
342         movw    %ax,%ss
343         sti
344         DATA32 ret      /* There is a 32 bit return address on the stack */
345         .code32
346
347 /**************************************************************************
348 GLOBAL DESCRIPTOR TABLE
349 **************************************************************************/
350         .align  4
351 _gdt:
352 gdtarg:
353         .word   0x27                    /* limit */
354         .long   _gdt                    /* addr */
355         .byte   0,0
356
357 _pmcs:
358         /* 32 bit protected mode code segment */
359         .word   0xffff,0
360         .byte   0,0x9f,0xcf,0
361
362 _pmds:
363         /* 32 bit protected mode data segment */
364         .word   0xffff,0
365         .byte   0,0x93,0xcf,0
366
367 _rmcs:
368         /* 16 bit real mode code segment */
369         .word   0xffff,(RELOC&0xffff)
370         .byte   (RELOC>>16),0x9b,0x00,(RELOC>>24)
371
372 _rmds:
373         /* 16 bit real mode data segment */
374         .word   0xffff,(RELOC&0xffff)
375         .byte   (RELOC>>16),0x93,0x00,(RELOC>>24)
376
377 gdtsave:        .long   0,0,0                   /* previous GDT */
378
379 /* Other variables */
380 initsp: .long   0