bios: Fix lowmem check
[syslinux.git] / core / pxelinux.asm
blob2c9b9f86483059c264bde66a003e91bc3f46abf0
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, BIOSName
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
71 BIOSName resw 1 ; Dummy variable - always 0
73 section .text16
74 global StackBuf
75 StackBuf equ STACK_TOP-44 ; Base of stack if we use our own
76 StackHome equ StackBuf
78 ; PXE loads the whole file, but assume it can't be more
79 ; than (384-31)K in size.
80 MaxLMA equ 384*1024
83 ; Primary entry point.
85 bootsec equ $
86 _start:
87 jmp 0:_start1 ; Canonicalize the address and skip
88 ; the patch header
91 ; Patch area for adding hardwired DHCP options
93 align 4
95 hcdhcp_magic dd 0x2983c8ac ; Magic number
96 hcdhcp_len dd 7*4 ; Size of this structure
97 hcdhcp_flags dd 0 ; Reserved for the future
98 ; Parameters to be parsed before the ones from PXE
99 bdhcp_offset dd 0 ; Offset (entered by patcher)
100 bdhcp_len dd 0 ; Length (entered by patcher)
101 ; Parameters to be parsed *after* the ones from PXE
102 adhcp_offset dd 0 ; Offset (entered by patcher)
103 adhcp_len dd 0 ; Length (entered by patcher)
105 _start1:
106 pushfd ; Paranoia... in case of return to PXE
107 pushad ; ... save as much state as possible
108 push ds
109 push es
110 push fs
111 push gs
113 cld ; Copy upwards
114 xor ax,ax
115 mov ds,ax
116 mov es,ax
118 %if 0 ; debugging code only... not intended for production use
119 ; Clobber the stack segment, to test for specific pathologies
120 mov di,STACK_BASE
121 mov cx,STACK_LEN >> 1
122 mov ax,0xf4f4
123 rep stosw
125 ; Clobber the tail of the 64K segment, too
126 extern __bss1_end
127 mov di,__bss1_end
128 sub cx,di ; CX = 0 previously
129 shr cx,1
130 rep stosw
131 %endif
133 ; That is all pushed onto the PXE stack. Save the pointer
134 ; to it and switch to an internal stack.
135 mov [InitStack],sp
136 mov [InitStack+2],ss
138 lss esp,[BaseStack]
139 sti ; Stack set up and ready
142 ; Initialize screen (if we're using one)
144 %include "init.inc"
147 ; Tell the user we got this far
149 mov si,syslinux_banner
150 call writestr_early
152 mov si,copyright_str
153 call writestr_early
156 ; do fs initialize
158 mov eax,ROOT_FS_OPS
159 xor ebp,ebp
160 pm_call fs_init
162 section .rodata
163 alignz 4
164 ROOT_FS_OPS:
165 extern pxe_fs_ops
166 dd pxe_fs_ops
167 dd 0
170 section .text16
172 ; Initialize the idle mechanism
174 extern reset_idle
175 pm_call reset_idle
178 ; Now we're all set to start with our *real* business.
180 ; In previous versions I avoided using 32-bit registers because of a
181 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
182 ; random. I figure, though, that if there are any of those still left
183 ; they probably won't be trying to install Linux on them...
185 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
186 ; to take'm out. In fact, we may want to put them back if we're going
187 ; to boot ELKS at some point.
191 ; Linux kernel loading code is common. However, we need to define
192 ; a couple of helper macros...
195 ; Unload PXE stack
196 %define HAVE_UNLOAD_PREP
197 %macro UNLOAD_PREP 0
198 pm_call unload_pxe
199 %endmacro
202 ; Jump to 32-bit ELF space
204 pm_call load_env32
205 jmp kaboom ; load_env32() shouldn't return. If it does, then kaboom!
207 print_hello:
208 enter_command:
209 auto_boot:
210 pm_call hello
213 ; Save hardwired DHCP options. This is done before the C environment
214 ; is initialized, so it has to be done in assembly.
216 %define MAX_DHCP_OPTS 4096
217 bits 32
219 section .savedata
220 global bdhcp_data, adhcp_data
221 bdhcp_data: resb MAX_DHCP_OPTS
222 adhcp_data: resb MAX_DHCP_OPTS
224 section .textnr
225 pm_save_data:
226 mov eax,MAX_DHCP_OPTS
227 movzx ecx,word [bdhcp_len]
228 cmp ecx,eax
229 jna .oksize
230 mov ecx,eax
231 mov [bdhcp_len],ax
232 .oksize:
233 mov esi,[bdhcp_offset]
234 add esi,_start
235 mov edi,bdhcp_data
236 add ecx,3
237 shr ecx,2
238 rep movsd
240 adhcp_copy:
241 movzx ecx,word [adhcp_len]
242 cmp ecx,eax
243 jna .oksize
244 mov ecx,eax
245 mov [adhcp_len],ax
246 .oksize:
247 mov esi,[adhcp_offset]
248 add esi,_start
249 mov edi,adhcp_data
250 add ecx,3
251 shr ecx,2
252 rep movsd
255 bits 16
257 ; As core/ui.inc used to be included here in core/pxelinux.asm, and it's no
258 ; longer used, its global variables that were previously used by
259 ; core/pxelinux.asm are now declared here.
260 section .bss16
261 alignb 4
262 Kernel_EAX resd 1
263 Kernel_SI resw 1
265 section .bss16
266 alignb 4
267 ThisKbdTo resd 1 ; Temporary holder for KbdTimeout
268 ThisTotalTo resd 1 ; Temporary holder for TotalTimeout
269 KernelExtPtr resw 1 ; During search, final null pointer
270 FuncFlag resb 1 ; Escape sequences received from keyboard
271 KernelType resb 1 ; Kernel type, from vkernel, if known
272 global KernelName
273 KernelName resb FILENAME_MAX ; Mangled name for kernel
275 section .text16
277 ; COMBOOT-loading code
279 %include "comboot.inc"
280 %include "com32.inc"
283 ; Boot sector loading code
287 ; Abort loading code
291 ; Hardware cleanup common code
294 section .text16
295 global local_boot16:function hidden
296 local_boot16:
297 mov [LocalBootType],ax
298 lss sp,[InitStack]
299 pop gs
300 pop fs
301 pop es
302 pop ds
303 popad
304 mov ax,[cs:LocalBootType]
305 cmp ax,-1 ; localboot -1 == INT 18h
306 je .int18
307 popfd
308 retf ; Return to PXE
309 .int18:
310 popfd
311 int 18h
312 jmp 0F000h:0FFF0h
316 ; kaboom: write a message and bail out. Wait for quite a while,
317 ; or a user keypress, then do a hard reboot.
319 ; Note: use BIOS_timer here; we may not have jiffies set up.
321 global kaboom
322 kaboom:
323 RESET_STACK_AND_SEGS AX
324 .patch: mov si,bailmsg
325 call writestr_early ; Returns with AL = 0
326 .drain: call pollchar
327 jz .drained
328 call getchar
329 jmp short .drain
330 .drained:
331 mov edi,[RebootTime]
332 mov al,[DHCPMagic]
333 and al,09h ; Magic+Timeout
334 cmp al,09h
335 je .time_set
336 mov edi,REBOOT_TIME
337 .time_set:
338 mov cx,18
339 .wait1: push cx
340 mov ecx,edi
341 .wait2: mov dx,[BIOS_timer]
342 .wait3: call pollchar
343 jnz .keypress
344 pm_call __idle
345 cmp dx,[BIOS_timer]
346 je .wait3
347 loop .wait2,ecx
348 mov al,'.'
349 pm_call pm_writechr
350 pop cx
351 loop .wait1
352 .keypress:
353 pm_call crlf
354 mov word [BIOS_magic],0 ; Cold reboot
355 jmp 0F000h:0FFF0h ; Reset vector address
358 ; pxenv
360 ; This is the main PXENV+/!PXE entry point, using the PXENV+
361 ; calling convention. This is a separate local routine so
362 ; we can hook special things from it if necessary. In particular,
363 ; some PXE stacks seem to not like being invoked from anything but
364 ; the initial stack, so humour it.
366 ; While we're at it, save and restore all registers.
368 global pxenv
369 pxenv:
370 pushfd
371 pushad
373 ; We may be removing ourselves from memory
374 cmp bx,PXENV_RESTART_TFTP
375 jz .disable_timer
376 cmp bx,PXENV_FILE_EXEC
377 jnz .store_stack
379 .disable_timer:
380 call timer_cleanup
382 .store_stack:
383 pushf
385 inc word [cs:PXEStackLock]
386 jnz .skip1
387 mov [cs:PXEStack],sp
388 mov [cs:PXEStack+2],ss
389 lss sp,[cs:InitStack]
390 .skip1:
391 popf
393 ; Pre-clear the Status field
394 mov word [es:di],cs
396 ; This works either for the PXENV+ or the !PXE calling
397 ; convention, as long as we ignore CF (which is redundant
398 ; with AX anyway.)
399 push es
400 push di
401 push bx
402 .jump: call 0:0
403 add sp,6
404 mov [cs:PXEStatus],ax
406 pushf
408 dec word [cs:PXEStackLock]
409 jns .skip2
410 lss sp,[cs:PXEStack]
411 .skip2:
412 popf
414 mov bp,sp
415 and ax,ax
416 setnz [bp+32] ; If AX != 0 set CF on return
418 ; This clobbers the AX return, but we already saved it into
419 ; the PXEStatus variable.
420 popad
422 ; If the call failed, it could return.
423 cmp bx,PXENV_RESTART_TFTP
424 jz .enable_timer
425 cmp bx,PXENV_FILE_EXEC
426 jnz .pop_flags
428 .enable_timer:
429 call timer_init
431 .pop_flags:
432 popfd ; Restore flags (incl. IF, DF)
435 ; Must be after function def due to NASM bug
436 global PXEEntry
437 PXEEntry equ pxenv.jump+1
440 ; The PXEStackLock keeps us from switching stacks if we take an interrupt
441 ; (which ends up calling pxenv) while we are already on the PXE stack.
442 ; It will be -1 normally, 0 inside a PXE call, and a positive value
443 ; inside a *nested* PXE call.
445 section .data16
446 alignb 2
447 PXEStackLock dw -1
449 section .bss16
450 alignb 2
451 PXEStatus resb 2
453 section .text16
455 ; Invoke INT 1Ah on the PXE stack. This is used by the "Plan C" method
456 ; for finding the PXE entry point.
458 global pxe_int1a
459 pxe_int1a:
460 mov [cs:PXEStack],sp
461 mov [cs:PXEStack+2],ss
462 lss sp,[cs:InitStack]
464 int 1Ah ; May trash registers
466 lss sp,[cs:PXEStack]
470 ; Special unload for gPXE: this switches the InitStack from
471 ; gPXE to the ROM PXE stack.
473 %if GPXE
474 global gpxe_unload
475 gpxe_unload:
476 mov bx,PXENV_FILE_EXIT_HOOK
477 mov di,pxe_file_exit_hook
478 call pxenv
479 jc .plain
481 ; Now we actually need to exit back to gPXE, which will
482 ; give control back to us on the *new* "original stack"...
483 pushfd
484 push ds
485 push es
486 mov [PXEStack],sp
487 mov [PXEStack+2],ss
488 lss sp,[InitStack]
489 pop gs
490 pop fs
491 pop es
492 pop ds
493 popad
494 popfd
495 xor ax,ax
496 retf
497 .resume:
500 ; gPXE will have a stack frame looking much like our
501 ; InitStack, except it has a magic cookie at the top,
502 ; and the segment registers are in reverse order.
503 pop eax
504 pop ax
505 pop bx
506 pop cx
507 pop dx
508 push ax
509 push bx
510 push cx
511 push dx
512 mov [cs:InitStack],sp
513 mov [cs:InitStack+2],ss
514 lss sp,[cs:PXEStack]
515 pop es
516 pop ds
517 popfd
519 .plain:
522 writestr_early:
523 pm_call pm_writestr
526 pollchar:
527 pm_call pm_pollchar
530 getchar:
531 pm_call pm_getchar
534 section .data16
535 alignz 4
536 pxe_file_exit_hook:
537 .status: dw 0
538 .offset: dw gpxe_unload.resume
539 .seg: dw 0
540 %endif
542 section .text16
544 ; -----------------------------------------------------------------------------
545 ; PXE modules
546 ; -----------------------------------------------------------------------------
548 %if IS_LPXELINUX
549 %include "pxeisr.inc"
550 %endif
552 ; -----------------------------------------------------------------------------
553 ; Common modules
554 ; -----------------------------------------------------------------------------
556 %include "common.inc" ; Universal modules
558 ; -----------------------------------------------------------------------------
559 ; Begin data section
560 ; -----------------------------------------------------------------------------
562 section .data16
564 global copyright_str, syslinux_banner
565 copyright_str db 'Copyright (C) 1994-'
566 asciidec YEAR
567 db ' H. Peter Anvin et al', CR, LF, 0
568 err_bootfailed db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0
569 bailmsg equ err_bootfailed
570 localboot_msg db 'Booting from local disk...', CR, LF, 0
571 syslinux_banner db CR, LF, MY_NAME, ' ', VERSION_STR, ' ', MY_TYPE, ' '
572 db DATE_STR, ' ', 0
575 ; Misc initialized (data) variables
577 section .data16
578 global KeepPXE
579 KeepPXE db 0 ; Should PXE be kept around?
582 ; IP information. Note that the field are in the same order as the
583 ; Linux kernel expects in the ip= option.
585 section .bss16
586 alignb 4
587 global IPInfo
588 IPInfo:
589 .IPv4 resd 1 ; IPv4 information
590 .MyIP resd 1 ; My IP address
591 .ServerIP resd 1
592 .GatewayIP resd 1
593 .Netmask resd 1