core, pxe: Don't push on one stack and pop from the other in pxenv
[syslinux.git] / core / pxelinux.asm
bloba2543dfe6d2d45212bec89931cd041ce6528ee54
1 ; -*- fundamental -*- (asm-mode sucks)
2 ; ****************************************************************************
4 ; pxelinux.asm
6 ; A program to boot Linux kernels off a TFTP server using the Intel PXE
7 ; network booting API. It is based on the SYSLINUX boot loader for
8 ; MS-DOS floppies.
10 ; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
11 ; Copyright 2009 Intel Corporation; author: H. Peter Anvin
13 ; This program is free software; you can redistribute it and/or modify
14 ; it under the terms of the GNU General Public License as published by
15 ; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
16 ; Boston MA 02111-1307, USA; either version 2 of the License, or
17 ; (at your option) any later version; incorporated herein by reference.
19 ; ****************************************************************************
21 %define IS_PXELINUX 1
22 %include "head.inc"
23 %include "pxe.inc"
25 ; gPXE extensions support
26 %define GPXE 1
29 ; Some semi-configurable constants... change on your own risk.
31 my_id equ pxelinux_id
32 NULLFILE equ 0 ; Zero byte == null file name
33 NULLOFFSET equ 0 ; Position in which to look
34 REBOOT_TIME equ 5*60 ; If failure, time until full reset
35 %assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the top
36 TFTP_BLOCKSIZE_LG2 equ 9 ; log2(bytes/block)
37 TFTP_BLOCKSIZE equ (1 << TFTP_BLOCKSIZE_LG2)
39 SECTOR_SHIFT equ TFTP_BLOCKSIZE_LG2
40 SECTOR_SIZE equ TFTP_BLOCKSIZE
42 ; ---------------------------------------------------------------------------
43 ; BEGIN CODE
44 ; ---------------------------------------------------------------------------
47 ; Memory below this point is reserved for the BIOS and the MBR
49 section .earlybss
50 global trackbuf
51 trackbufsize equ 8192
52 trackbuf resb trackbufsize ; Track buffer goes here
53 ; ends at 2800h
55 ; These fields save information from before the time
56 ; .bss is zeroed... must be in .earlybss
57 global InitStack
58 InitStack resd 1
60 section .bss16
61 alignb FILENAME_MAX
62 PXEStack resd 1 ; Saved stack during PXE call
64 alignb 4
65 global DHCPMagic, RebootTime, BIOSName
66 RebootTime resd 1 ; Reboot timeout, if set by option
67 LocalBootType resw 1 ; Local boot return code
68 DHCPMagic resb 1 ; PXELINUX magic flags
69 BIOSName resw 1 ; Dummy variable - always 0
71 section .text16
72 global StackBuf
73 StackBuf equ STACK_TOP-44 ; Base of stack if we use our own
74 StackHome equ StackBuf
76 ; PXE loads the whole file, but assume it can't be more
77 ; than (384-31)K in size.
78 MaxLMA equ 384*1024
81 ; Primary entry point.
83 bootsec equ $
84 _start:
85 jmp 0:_start1 ; Canonicalize the address and skip
86 ; the patch header
89 ; Patch area for adding hardwired DHCP options
91 align 4
93 hcdhcp_magic dd 0x2983c8ac ; Magic number
94 hcdhcp_len dd 7*4 ; Size of this structure
95 hcdhcp_flags dd 0 ; Reserved for the future
96 global bdhcp_len, adhcp_len
97 ; Parameters to be parsed before the ones from PXE
98 bdhcp_offset dd 0 ; Offset (entered by patcher)
99 bdhcp_len dd 0 ; Length (entered by patcher)
100 ; Parameters to be parsed *after* the ones from PXE
101 adhcp_offset dd 0 ; Offset (entered by patcher)
102 adhcp_len dd 0 ; Length (entered by patcher)
104 _start1:
105 pushfd ; Paranoia... in case of return to PXE
106 pushad ; ... save as much state as possible
107 push ds
108 push es
109 push fs
110 push gs
112 cld ; Copy upwards
113 xor ax,ax
114 mov ds,ax
115 mov es,ax
117 %if 0 ; debugging code only... not intended for production use
118 ; Clobber the stack segment, to test for specific pathologies
119 mov di,STACK_BASE
120 mov cx,STACK_LEN >> 1
121 mov ax,0xf4f4
122 rep stosw
124 ; Clobber the tail of the 64K segment, too
125 extern __bss1_end
126 mov di,__bss1_end
127 sub cx,di ; CX = 0 previously
128 shr cx,1
129 rep stosw
130 %endif
132 ; That is all pushed onto the PXE stack. Save the pointer
133 ; to it and switch to an internal stack.
134 mov [InitStack],sp
135 mov [InitStack+2],ss
137 lss esp,[BaseStack]
138 sti ; Stack set up and ready
141 ; Initialize screen (if we're using one)
143 %include "init.inc"
146 ; Tell the user we got this far
148 mov si,syslinux_banner
149 call writestr_early
151 mov si,copyright_str
152 call writestr_early
155 ; do fs initialize
157 mov eax,ROOT_FS_OPS
158 xor ebp,ebp
159 pm_call pm_fs_init
161 section .rodata
162 alignz 4
163 ROOT_FS_OPS:
164 extern pxe_fs_ops
165 dd pxe_fs_ops
166 dd 0
169 section .text16
171 ; Initialize the idle mechanism
173 extern reset_idle
174 pm_call reset_idle
177 ; Now we're all set to start with our *real* business.
179 ; In previous versions I avoided using 32-bit registers because of a
180 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
181 ; random. I figure, though, that if there are any of those still left
182 ; they probably won't be trying to install Linux on them...
184 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
185 ; to take'm out. In fact, we may want to put them back if we're going
186 ; to boot ELKS at some point.
190 ; Linux kernel loading code is common. However, we need to define
191 ; a couple of helper macros...
194 ; Unload PXE stack
195 %define HAVE_UNLOAD_PREP
196 %macro UNLOAD_PREP 0
197 pm_call unload_pxe
198 %endmacro
201 ; Jump to 32-bit ELF space
203 pm_call load_env32
204 jmp kaboom ; load_env32() shouldn't return. If it does, then kaboom!
206 print_hello:
207 enter_command:
208 auto_boot:
209 pm_call hello
212 ; Save hardwired DHCP options. This is done before the C environment
213 ; is initialized, so it has to be done in assembly.
215 %define MAX_DHCP_OPTS 4096
216 bits 32
218 section .savedata
219 global bdhcp_data, adhcp_data
220 bdhcp_data: resb MAX_DHCP_OPTS
221 adhcp_data: resb MAX_DHCP_OPTS
223 section .textnr
224 pm_save_data:
225 mov eax,MAX_DHCP_OPTS
226 movzx ecx,word [bdhcp_len]
227 cmp ecx,eax
228 jna .oksize
229 mov ecx,eax
230 mov [bdhcp_len],ax
231 .oksize:
232 mov esi,[bdhcp_offset]
233 add esi,_start
234 mov edi,bdhcp_data
235 add ecx,3
236 shr ecx,2
237 rep movsd
239 adhcp_copy:
240 movzx ecx,word [adhcp_len]
241 cmp ecx,eax
242 jna .oksize
243 mov ecx,eax
244 mov [adhcp_len],ax
245 .oksize:
246 mov esi,[adhcp_offset]
247 add esi,_start
248 mov edi,adhcp_data
249 add ecx,3
250 shr ecx,2
251 rep movsd
254 bits 16
256 ; As core/ui.inc used to be included here in core/pxelinux.asm, and it's no
257 ; longer used, its global variables that were previously used by
258 ; core/pxelinux.asm are now declared here.
259 section .bss16
260 alignb 4
261 Kernel_EAX resd 1
262 Kernel_SI resw 1
264 section .bss16
265 alignb 4
266 ThisKbdTo resd 1 ; Temporary holder for KbdTimeout
267 ThisTotalTo resd 1 ; Temporary holder for TotalTimeout
268 KernelExtPtr resw 1 ; During search, final null pointer
269 FuncFlag resb 1 ; Escape sequences received from keyboard
270 KernelType resb 1 ; Kernel type, from vkernel, if known
271 global KernelName
272 KernelName resb FILENAME_MAX ; Mangled name for kernel
274 section .text16
276 ; COM32 vestigial data structure
278 %include "com32.inc"
280 section .text16
281 global local_boot16:function hidden
282 local_boot16:
283 mov [LocalBootType],ax
284 lss sp,[InitStack]
285 pop gs
286 pop fs
287 pop es
288 pop ds
289 popad
290 mov ax,[cs:LocalBootType]
291 cmp ax,-1 ; localboot -1 == INT 18h
292 je .int18
293 popfd
294 retf ; Return to PXE
295 .int18:
296 popfd
297 int 18h
298 jmp 0F000h:0FFF0h
302 ; kaboom: write a message and bail out. Wait for quite a while,
303 ; or a user keypress, then do a hard reboot.
305 ; Note: use BIOS_timer here; we may not have jiffies set up.
307 global kaboom
308 kaboom:
309 RESET_STACK_AND_SEGS AX
310 .patch: mov si,bailmsg
311 call writestr_early ; Returns with AL = 0
312 .drain: call pollchar
313 jz .drained
314 call getchar
315 jmp short .drain
316 .drained:
317 mov edi,[RebootTime]
318 mov al,[DHCPMagic]
319 and al,09h ; Magic+Timeout
320 cmp al,09h
321 je .time_set
322 mov edi,REBOOT_TIME
323 .time_set:
324 mov cx,18
325 .wait1: push cx
326 mov ecx,edi
327 .wait2: mov dx,[BIOS_timer]
328 .wait3: call pollchar
329 jnz .keypress
330 pm_call __idle
331 cmp dx,[BIOS_timer]
332 je .wait3
333 loop .wait2,ecx
334 mov al,'.'
335 pm_call pm_writechr
336 pop cx
337 loop .wait1
338 .keypress:
339 pm_call crlf
340 mov word [BIOS_magic],0 ; Cold reboot
341 jmp 0F000h:0FFF0h ; Reset vector address
344 ; pxenv
346 ; This is the main PXENV+/!PXE entry point, using the PXENV+
347 ; calling convention. This is a separate local routine so
348 ; we can hook special things from it if necessary. In particular,
349 ; some PXE stacks seem to not like being invoked from anything but
350 ; the initial stack, so humour it.
352 ; While we're at it, save and restore all registers.
354 global pxenv
355 pxenv:
356 pushfd
357 pushad
359 ; We may be removing ourselves from memory
360 cmp bx,PXENV_RESTART_TFTP
361 jz .disable_timer
362 cmp bx,PXENV_FILE_EXEC
363 jnz .store_stack
365 .disable_timer:
366 call bios_timer_cleanup
368 .store_stack:
369 pushf
371 inc word [cs:PXEStackLock]
372 jnz .skip1
373 pop bp
374 mov [cs:PXEStack],sp
375 mov [cs:PXEStack+2],ss
376 lss sp,[cs:InitStack]
377 push bp
378 .skip1:
379 popf
381 ; Pre-clear the Status field
382 mov word [es:di],cs
384 ; This works either for the PXENV+ or the !PXE calling
385 ; convention, as long as we ignore CF (which is redundant
386 ; with AX anyway.)
387 push es
388 push di
389 push bx
390 .jump: call 0:0
391 add sp,6
392 mov [cs:PXEStatus],ax
394 pushf
396 dec word [cs:PXEStackLock]
397 jns .skip2
398 pop bp
399 lss sp,[cs:PXEStack]
400 push bp
401 .skip2:
402 popf
404 mov bp,sp
405 and ax,ax
406 setnz [bp+32] ; If AX != 0 set CF on return
408 ; This clobbers the AX return, but we already saved it into
409 ; the PXEStatus variable.
410 popad
412 ; If the call failed, it could return.
413 cmp bx,PXENV_RESTART_TFTP
414 jz .enable_timer
415 cmp bx,PXENV_FILE_EXEC
416 jnz .pop_flags
418 .enable_timer:
419 call timer_init
421 .pop_flags:
422 popfd ; Restore flags (incl. IF, DF)
425 ; Must be after function def due to NASM bug
426 global PXEEntry
427 PXEEntry equ pxenv.jump+1
430 ; The PXEStackLock keeps us from switching stacks if we take an interrupt
431 ; (which ends up calling pxenv) while we are already on the PXE stack.
432 ; It will be -1 normally, 0 inside a PXE call, and a positive value
433 ; inside a *nested* PXE call.
435 section .data16
436 alignb 2
437 PXEStackLock dw -1
439 section .bss16
440 alignb 2
441 PXEStatus resb 2
443 section .text16
445 ; Invoke INT 1Ah on the PXE stack. This is used by the "Plan C" method
446 ; for finding the PXE entry point.
448 global pxe_int1a
449 pxe_int1a:
450 mov [cs:PXEStack],sp
451 mov [cs:PXEStack+2],ss
452 lss sp,[cs:InitStack]
454 int 1Ah ; May trash registers
456 lss sp,[cs:PXEStack]
460 ; Special unload for gPXE: this switches the InitStack from
461 ; gPXE to the ROM PXE stack.
463 %if GPXE
464 global gpxe_unload
465 gpxe_unload:
466 mov bx,PXENV_FILE_EXIT_HOOK
467 mov di,pxe_file_exit_hook
468 call pxenv
469 jc .plain
471 ; Now we actually need to exit back to gPXE, which will
472 ; give control back to us on the *new* "original stack"...
473 pushfd
474 push ds
475 push es
476 mov [PXEStack],sp
477 mov [PXEStack+2],ss
478 lss sp,[InitStack]
479 pop gs
480 pop fs
481 pop es
482 pop ds
483 popad
484 popfd
485 xor ax,ax
486 retf
487 .resume:
490 ; gPXE will have a stack frame looking much like our
491 ; InitStack, except it has a magic cookie at the top,
492 ; and the segment registers are in reverse order.
493 pop eax
494 pop ax
495 pop bx
496 pop cx
497 pop dx
498 push ax
499 push bx
500 push cx
501 push dx
502 mov [cs:InitStack],sp
503 mov [cs:InitStack+2],ss
504 lss sp,[cs:PXEStack]
505 pop es
506 pop ds
507 popfd
509 .plain:
512 writestr_early:
513 pm_call pm_writestr
516 pollchar:
517 pm_call pm_pollchar
520 getchar:
521 pm_call pm_getchar
524 section .data16
525 alignz 4
526 pxe_file_exit_hook:
527 .status: dw 0
528 .offset: dw gpxe_unload.resume
529 .seg: dw 0
530 %endif
532 section .text16
534 ; -----------------------------------------------------------------------------
535 ; PXE modules
536 ; -----------------------------------------------------------------------------
538 %if IS_LPXELINUX
539 %include "pxeisr.inc"
540 %endif
542 ; -----------------------------------------------------------------------------
543 ; Common modules
544 ; -----------------------------------------------------------------------------
546 %include "common.inc" ; Universal modules
548 ; -----------------------------------------------------------------------------
549 ; Begin data section
550 ; -----------------------------------------------------------------------------
552 section .data16
554 global copyright_str, syslinux_banner
555 copyright_str db 'Copyright (C) 1994-'
556 asciidec YEAR
557 db ' H. Peter Anvin et al', CR, LF, 0
558 err_bootfailed db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0
559 bailmsg equ err_bootfailed
560 localboot_msg db 'Booting from local disk...', CR, LF, 0
561 syslinux_banner db CR, LF, MY_NAME, ' ', VERSION_STR, ' ', MY_TYPE, ' '
562 db DATE_STR, ' ', 0
565 ; Misc initialized (data) variables
567 section .data16
568 global KeepPXE
569 KeepPXE db 0 ; Should PXE be kept around?
571 section .bss16
572 global OrigFDCTabPtr
573 OrigFDCTabPtr resd 1 ; Keep bios_cleanup_hardware() honest