Add memtest support.
[syslinux-debian/hramrach.git] / core / pxelinux.asm
blob64194d382e75fc3c6b86e68b9bb64c4527807b38
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, StrucPtr, BIOSName
66 RebootTime resd 1 ; Reboot timeout, if set by option
67 StrucPtr resw 2 ; Pointer to PXENV+ or !PXE structure
68 LocalBootType resw 1 ; Local boot return code
69 DHCPMagic resb 1 ; PXELINUX magic flags
70 BIOSName resw 1 ; Dummy variable - always 0
72 section .text16
73 global StackBuf
74 StackBuf equ STACK_TOP-44 ; Base of stack if we use our own
75 StackHome equ StackBuf
77 ; PXE loads the whole file, but assume it can't be more
78 ; than (384-31)K in size.
79 MaxLMA equ 384*1024
82 ; Primary entry point.
84 bootsec equ $
85 _start:
86 jmp 0:_start1 ; Canonicalize the address and skip
87 ; the patch header
90 ; Patch area for adding hardwired DHCP options
92 align 4
94 hcdhcp_magic dd 0x2983c8ac ; Magic number
95 hcdhcp_len dd 7*4 ; Size of this structure
96 hcdhcp_flags dd 0 ; Reserved for the future
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 ; COMBOOT-loading code
278 %include "comboot.inc"
279 %include "com32.inc"
282 ; Boot sector loading code
286 ; Abort loading code
290 ; Hardware cleanup common code
293 section .text16
294 global local_boot16:function hidden
295 local_boot16:
296 mov [LocalBootType],ax
297 lss sp,[InitStack]
298 pop gs
299 pop fs
300 pop es
301 pop ds
302 popad
303 mov ax,[cs:LocalBootType]
304 cmp ax,-1 ; localboot -1 == INT 18h
305 je .int18
306 popfd
307 retf ; Return to PXE
308 .int18:
309 popfd
310 int 18h
311 jmp 0F000h:0FFF0h
315 ; kaboom: write a message and bail out. Wait for quite a while,
316 ; or a user keypress, then do a hard reboot.
318 ; Note: use BIOS_timer here; we may not have jiffies set up.
320 global kaboom
321 kaboom:
322 RESET_STACK_AND_SEGS AX
323 .patch: mov si,bailmsg
324 call writestr_early ; Returns with AL = 0
325 .drain: call pollchar
326 jz .drained
327 call getchar
328 jmp short .drain
329 .drained:
330 mov edi,[RebootTime]
331 mov al,[DHCPMagic]
332 and al,09h ; Magic+Timeout
333 cmp al,09h
334 je .time_set
335 mov edi,REBOOT_TIME
336 .time_set:
337 mov cx,18
338 .wait1: push cx
339 mov ecx,edi
340 .wait2: mov dx,[BIOS_timer]
341 .wait3: call pollchar
342 jnz .keypress
343 pm_call __idle
344 cmp dx,[BIOS_timer]
345 je .wait3
346 loop .wait2,ecx
347 mov al,'.'
348 pm_call pm_writechr
349 pop cx
350 loop .wait1
351 .keypress:
352 pm_call crlf
353 mov word [BIOS_magic],0 ; Cold reboot
354 jmp 0F000h:0FFF0h ; Reset vector address
357 ; pxenv
359 ; This is the main PXENV+/!PXE entry point, using the PXENV+
360 ; calling convention. This is a separate local routine so
361 ; we can hook special things from it if necessary. In particular,
362 ; some PXE stacks seem to not like being invoked from anything but
363 ; the initial stack, so humour it.
365 ; While we're at it, save and restore all registers.
367 global pxenv
368 pxenv:
369 pushfd
370 pushad
372 ; We may be removing ourselves from memory
373 cmp bx,PXENV_RESTART_TFTP
374 jz .disable_timer
375 cmp bx,PXENV_FILE_EXEC
376 jnz .store_stack
378 .disable_timer:
379 call bios_timer_cleanup
381 .store_stack:
382 pushf
384 inc word [cs:PXEStackLock]
385 jnz .skip1
386 mov [cs:PXEStack],sp
387 mov [cs:PXEStack+2],ss
388 lss sp,[cs:InitStack]
389 .skip1:
390 popf
392 ; Pre-clear the Status field
393 mov word [es:di],cs
395 ; This works either for the PXENV+ or the !PXE calling
396 ; convention, as long as we ignore CF (which is redundant
397 ; with AX anyway.)
398 push es
399 push di
400 push bx
401 .jump: call 0:0
402 add sp,6
403 mov [cs:PXEStatus],ax
405 pushf
407 dec word [cs:PXEStackLock]
408 jns .skip2
409 lss sp,[cs:PXEStack]
410 .skip2:
411 popf
413 mov bp,sp
414 and ax,ax
415 setnz [bp+32] ; If AX != 0 set CF on return
417 ; This clobbers the AX return, but we already saved it into
418 ; the PXEStatus variable.
419 popad
421 ; If the call failed, it could return.
422 cmp bx,PXENV_RESTART_TFTP
423 jz .enable_timer
424 cmp bx,PXENV_FILE_EXEC
425 jnz .pop_flags
427 .enable_timer:
428 call timer_init
430 .pop_flags:
431 popfd ; Restore flags (incl. IF, DF)
434 ; Must be after function def due to NASM bug
435 global PXEEntry
436 PXEEntry equ pxenv.jump+1
439 ; The PXEStackLock keeps us from switching stacks if we take an interrupt
440 ; (which ends up calling pxenv) while we are already on the PXE stack.
441 ; It will be -1 normally, 0 inside a PXE call, and a positive value
442 ; inside a *nested* PXE call.
444 section .data16
445 alignb 2
446 PXEStackLock dw -1
448 section .bss16
449 alignb 2
450 PXEStatus resb 2
452 section .text16
454 ; Invoke INT 1Ah on the PXE stack. This is used by the "Plan C" method
455 ; for finding the PXE entry point.
457 global pxe_int1a
458 pxe_int1a:
459 mov [cs:PXEStack],sp
460 mov [cs:PXEStack+2],ss
461 lss sp,[cs:InitStack]
463 int 1Ah ; May trash registers
465 lss sp,[cs:PXEStack]
469 ; Special unload for gPXE: this switches the InitStack from
470 ; gPXE to the ROM PXE stack.
472 %if GPXE
473 global gpxe_unload
474 gpxe_unload:
475 mov bx,PXENV_FILE_EXIT_HOOK
476 mov di,pxe_file_exit_hook
477 call pxenv
478 jc .plain
480 ; Now we actually need to exit back to gPXE, which will
481 ; give control back to us on the *new* "original stack"...
482 pushfd
483 push ds
484 push es
485 mov [PXEStack],sp
486 mov [PXEStack+2],ss
487 lss sp,[InitStack]
488 pop gs
489 pop fs
490 pop es
491 pop ds
492 popad
493 popfd
494 xor ax,ax
495 retf
496 .resume:
499 ; gPXE will have a stack frame looking much like our
500 ; InitStack, except it has a magic cookie at the top,
501 ; and the segment registers are in reverse order.
502 pop eax
503 pop ax
504 pop bx
505 pop cx
506 pop dx
507 push ax
508 push bx
509 push cx
510 push dx
511 mov [cs:InitStack],sp
512 mov [cs:InitStack+2],ss
513 lss sp,[cs:PXEStack]
514 pop es
515 pop ds
516 popfd
518 .plain:
521 writestr_early:
522 pm_call pm_writestr
525 pollchar:
526 pm_call pm_pollchar
529 getchar:
530 pm_call pm_getchar
533 section .data16
534 alignz 4
535 pxe_file_exit_hook:
536 .status: dw 0
537 .offset: dw gpxe_unload.resume
538 .seg: dw 0
539 %endif
541 section .text16
543 ; -----------------------------------------------------------------------------
544 ; PXE modules
545 ; -----------------------------------------------------------------------------
547 %if IS_LPXELINUX
548 %include "pxeisr.inc"
549 %endif
551 ; -----------------------------------------------------------------------------
552 ; Common modules
553 ; -----------------------------------------------------------------------------
555 %include "common.inc" ; Universal modules
557 ; -----------------------------------------------------------------------------
558 ; Begin data section
559 ; -----------------------------------------------------------------------------
561 section .data16
563 global copyright_str, syslinux_banner
564 copyright_str db 'Copyright (C) 1994-'
565 asciidec YEAR
566 db ' H. Peter Anvin et al', CR, LF, 0
567 err_bootfailed db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0
568 bailmsg equ err_bootfailed
569 localboot_msg db 'Booting from local disk...', CR, LF, 0
570 syslinux_banner db CR, LF, MY_NAME, ' ', VERSION_STR, ' ', MY_TYPE, ' '
571 db DATE_STR, ' ', 0
574 ; Misc initialized (data) variables
576 section .data16
577 global KeepPXE
578 KeepPXE db 0 ; Should PXE be kept around?