pxe: use core for atomic modification of certain volatiles
[syslinux/sherbszt.git] / core / pxelinux.asm
blobd39234d179d5661766f3701af96c303247e16756
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, APIVer
66 RebootTime resd 1 ; Reboot timeout, if set by option
67 StrucPtr resw 2 ; Pointer to PXENV+ or !PXE structure
68 APIVer resw 1 ; PXE API version found
69 LocalBootType resw 1 ; Local boot return code
70 DHCPMagic resb 1 ; PXELINUX magic flags
72 section .text16
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 ; Parameters to be parsed before the ones from PXE
97 bdhcp_offset dd 0 ; Offset (entered by patcher)
98 bdhcp_len dd 0 ; Length (entered by patcher)
99 ; Parameters to be parsed *after* the ones from PXE
100 adhcp_offset dd 0 ; Offset (entered by patcher)
101 adhcp_len dd 0 ; Length (entered by patcher)
103 _start1:
104 pushfd ; Paranoia... in case of return to PXE
105 pushad ; ... save as much state as possible
106 push ds
107 push es
108 push fs
109 push gs
111 cld ; Copy upwards
112 xor ax,ax
113 mov ds,ax
114 mov es,ax
116 %if 0 ; debugging code only... not intended for production use
117 ; Clobber the stack segment, to test for specific pathologies
118 mov di,STACK_BASE
119 mov cx,STACK_LEN >> 1
120 mov ax,0xf4f4
121 rep stosw
123 ; Clobber the tail of the 64K segment, too
124 extern __bss1_end
125 mov di,__bss1_end
126 sub cx,di ; CX = 0 previously
127 shr cx,1
128 rep stosw
129 %endif
131 ; That is all pushed onto the PXE stack. Save the pointer
132 ; to it and switch to an internal stack.
133 mov [InitStack],sp
134 mov [InitStack+2],ss
136 lss esp,[BaseStack]
137 sti ; Stack set up and ready
139 ; Move the hardwired DHCP options (if present) to a safe place...
141 bdhcp_copy:
142 mov cx,[bdhcp_len]
143 mov ax,trackbufsize/2
144 jcxz .none
145 cmp cx,ax
146 jbe .oksize
147 mov cx,ax
148 mov [bdhcp_len],ax
149 .oksize:
150 mov eax,[bdhcp_offset]
151 add eax,_start
152 mov si,ax
153 and si,000Fh
154 shr eax,4
155 push ds
156 mov ds,ax
157 mov di,trackbuf
158 add cx,3
159 shr cx,2
160 rep movsd
161 pop ds
162 .none:
164 adhcp_copy:
165 mov cx,[adhcp_len]
166 mov ax,trackbufsize/2
167 jcxz .none
168 cmp cx,ax
169 jbe .oksize
170 mov cx,ax
171 mov [adhcp_len],ax
172 .oksize:
173 mov eax,[adhcp_offset]
174 add eax,_start
175 mov si,ax
176 and si,000Fh
177 shr eax,4
178 push ds
179 mov ds,ax
180 mov di,trackbuf+trackbufsize/2
181 add cx,3
182 shr cx,2
183 rep movsd
184 pop ds
185 .none:
188 ; Initialize screen (if we're using one)
190 %include "init.inc"
193 ; Tell the user we got this far
195 mov si,syslinux_banner
196 call writestr_early
198 mov si,copyright_str
199 call writestr_early
202 ; do fs initialize
204 mov eax,ROOT_FS_OPS
205 xor ebp,ebp
206 pm_call fs_init
208 section .rodata
209 alignz 4
210 ROOT_FS_OPS:
211 extern pxe_fs_ops
212 dd pxe_fs_ops
213 dd 0
216 section .text16
218 ; Initialize the idle mechanism
220 call reset_idle
223 ; Now we're all set to start with our *real* business. First load the
224 ; configuration file (if any) and parse it.
226 ; In previous versions I avoided using 32-bit registers because of a
227 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
228 ; random. I figure, though, that if there are any of those still left
229 ; they probably won't be trying to install Linux on them...
231 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
232 ; to take'm out. In fact, we may want to put them back if we're going
233 ; to boot ELKS at some point.
237 ; Linux kernel loading code is common. However, we need to define
238 ; a couple of helper macros...
241 ; Unload PXE stack
242 %define HAVE_UNLOAD_PREP
243 %macro UNLOAD_PREP 0
244 pm_call unload_pxe
245 %endmacro
248 ; Load configuration file
250 pm_call pm_load_config
251 jz no_config_file
254 ; Now we have the config file open. Parse the config file and
255 ; run the user interface.
257 %include "ui.inc"
260 ; Boot to the local disk by returning the appropriate PXE magic.
261 ; AX contains the appropriate return code.
263 local_boot:
264 push cs
265 pop ds
266 mov [LocalBootType],ax
267 call vgaclearmode
268 mov si,localboot_msg
269 call writestr_early
270 ; Restore the environment we were called with
271 pm_call reset_pxe
272 call cleanup_hardware
273 lss sp,[InitStack]
274 pop gs
275 pop fs
276 pop es
277 pop ds
278 popad
279 mov ax,[cs:LocalBootType]
280 cmp ax,-1 ; localboot -1 == INT 18h
281 je .int18
282 popfd
283 retf ; Return to PXE
284 .int18:
285 popfd
286 int 18h
287 jmp 0F000h:0FFF0h
291 ; kaboom: write a message and bail out. Wait for quite a while,
292 ; or a user keypress, then do a hard reboot.
294 ; Note: use BIOS_timer here; we may not have jiffies set up.
296 global kaboom
297 kaboom:
298 RESET_STACK_AND_SEGS AX
299 .patch: mov si,bailmsg
300 call writestr_early ; Returns with AL = 0
301 .drain: call pollchar
302 jz .drained
303 call getchar
304 jmp short .drain
305 .drained:
306 mov edi,[RebootTime]
307 mov al,[DHCPMagic]
308 and al,09h ; Magic+Timeout
309 cmp al,09h
310 je .time_set
311 mov edi,REBOOT_TIME
312 .time_set:
313 mov cx,18
314 .wait1: push cx
315 mov ecx,edi
316 .wait2: mov dx,[BIOS_timer]
317 .wait3: call pollchar
318 jnz .keypress
319 call do_idle
320 cmp dx,[BIOS_timer]
321 je .wait3
322 loop .wait2,ecx
323 mov al,'.'
324 call writechr
325 pop cx
326 loop .wait1
327 .keypress:
328 call crlf
329 mov word [BIOS_magic],0 ; Cold reboot
330 jmp 0F000h:0FFF0h ; Reset vector address
334 ; pxenv
336 ; This is the main PXENV+/!PXE entry point, using the PXENV+
337 ; calling convention. This is a separate local routine so
338 ; we can hook special things from it if necessary. In particular,
339 ; some PXE stacks seem to not like being invoked from anything but
340 ; the initial stack, so humour it.
342 ; While we're at it, save and restore all registers.
344 global pxenv
345 pxenv:
346 pushfd
347 pushad
349 ; We may be removing ourselves from memory
350 cmp bx,0073h ; PXENV_RESTART_TFTP
351 jz .disable_timer
352 cmp bx,00E5h ; gPXE PXENV_FILE_EXEC
353 jnz .store_stack
355 .disable_timer:
356 call timer_cleanup
358 .store_stack:
359 pushf
361 inc word [cs:PXEStackLock]
362 jnz .skip1
363 mov [cs:PXEStack],sp
364 mov [cs:PXEStack+2],ss
365 lss sp,[cs:InitStack]
366 .skip1:
367 popf
369 ; Pre-clear the Status field
370 mov word [es:di],cs
372 ; This works either for the PXENV+ or the !PXE calling
373 ; convention, as long as we ignore CF (which is redundant
374 ; with AX anyway.)
375 push es
376 push di
377 push bx
378 .jump: call 0:0
379 add sp,6
380 mov [cs:PXEStatus],ax
382 pushf
384 dec word [cs:PXEStackLock]
385 jns .skip2
386 lss sp,[cs:PXEStack]
387 .skip2:
388 popf
390 mov bp,sp
391 and ax,ax
392 setnz [bp+32] ; If AX != 0 set CF on return
394 ; This clobbers the AX return, but we already saved it into
395 ; the PXEStatus variable.
396 popad
398 ; If the call failed, it could return.
399 cmp bx,0073h
400 jz .enable_timer
401 cmp bx,00E5h
402 jnz .pop_flags
404 .enable_timer:
405 call timer_init
407 .pop_flags:
408 popfd ; Restore flags (incl. IF, DF)
411 ; Must be after function def due to NASM bug
412 global PXEEntry
413 PXEEntry equ pxenv.jump+1
416 ; The PXEStackLock keeps us from switching stacks if we take an interrupt
417 ; (which ends up calling pxenv) while we are already on the PXE stack.
418 ; It will be -1 normally, 0 inside a PXE call, and a positive value
419 ; inside a *nested* PXE call.
421 section .data16
422 alignb 2
423 PXEStackLock dw -1
425 section .bss16
426 alignb 2
427 PXEStatus resb 2
429 section .text16
431 ; Invoke INT 1Ah on the PXE stack. This is used by the "Plan C" method
432 ; for finding the PXE entry point.
434 global pxe_int1a
435 pxe_int1a:
436 mov [cs:PXEStack],sp
437 mov [cs:PXEStack+2],ss
438 lss sp,[cs:InitStack]
440 int 1Ah ; May trash registers
442 lss sp,[cs:PXEStack]
446 ; Special unload for gPXE: this switches the InitStack from
447 ; gPXE to the ROM PXE stack.
449 %if GPXE
450 global gpxe_unload
451 gpxe_unload:
452 mov bx,PXENV_FILE_EXIT_HOOK
453 mov di,pxe_file_exit_hook
454 call pxenv
455 jc .plain
457 ; Now we actually need to exit back to gPXE, which will
458 ; give control back to us on the *new* "original stack"...
459 pushfd
460 push ds
461 push es
462 mov [PXEStack],sp
463 mov [PXEStack+2],ss
464 lss sp,[InitStack]
465 pop gs
466 pop fs
467 pop es
468 pop ds
469 popad
470 popfd
471 xor ax,ax
472 retf
473 .resume:
476 ; gPXE will have a stack frame looking much like our
477 ; InitStack, except it has a magic cookie at the top,
478 ; and the segment registers are in reverse order.
479 pop eax
480 pop ax
481 pop bx
482 pop cx
483 pop dx
484 push ax
485 push bx
486 push cx
487 push dx
488 mov [cs:InitStack],sp
489 mov [cs:InitStack+2],ss
490 lss sp,[cs:PXEStack]
491 pop es
492 pop ds
493 popfd
495 .plain:
498 section .data16
499 alignz 4
500 pxe_file_exit_hook:
501 .status: dw 0
502 .offset: dw gpxe_unload.resume
503 .seg: dw 0
504 %endif
506 section .text16
508 ; -----------------------------------------------------------------------------
509 ; PXE modules
510 ; -----------------------------------------------------------------------------
512 %include "pxeisr.inc"
514 ; -----------------------------------------------------------------------------
515 ; Common modules
516 ; -----------------------------------------------------------------------------
518 %include "common.inc" ; Universal modules
519 %include "writestr.inc" ; String output
520 writestr_early equ writestr
521 %include "writehex.inc" ; Hexadecimal output
522 %include "rawcon.inc" ; Console I/O w/o using the console functions
524 ; -----------------------------------------------------------------------------
525 ; Begin data section
526 ; -----------------------------------------------------------------------------
528 section .data16
530 copyright_str db ' Copyright (C) 1994-'
531 asciidec YEAR
532 db ' H. Peter Anvin et al', CR, LF, 0
533 err_bootfailed db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0
534 bailmsg equ err_bootfailed
535 localboot_msg db 'Booting from local disk...', CR, LF, 0
536 syslinux_banner db CR, LF, MY_NAME, ' ', VERSION_STR, ' ', DATE_STR, ' ', 0
539 ; Config file keyword table
541 %include "keywords.inc"
544 ; Extensions to search for (in *forward* order).
545 ; (.bs and .bss16 are disabled for PXELINUX, since they are not supported)
547 alignz 4
548 exten_table: db '.cbt' ; COMBOOT (specific)
549 db '.0', 0, 0 ; PXE bootstrap program
550 db '.com' ; COMBOOT (same as DOS)
551 db '.c32' ; COM32
552 exten_table_end:
553 dd 0, 0 ; Need 8 null bytes here
556 ; Misc initialized (data) variables
558 section .data16
559 global KeepPXE
560 KeepPXE db 0 ; Should PXE be kept around?
563 ; IP information. Note that the field are in the same order as the
564 ; Linux kernel expects in the ip= option.
566 section .bss16
567 alignb 4
568 global IPInfo
569 IPInfo:
570 .IPv4 resd 1 ; IPv4 information
571 .MyIP resd 1 ; My IP address
572 .ServerIP resd 1
573 .GatewayIP resd 1
574 .Netmask resd 1