• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1; -*- fundamental -*- (asm-mode sucks)
2; ****************************************************************************
3;
4;  pxelinux.asm
5;
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.
9;
10;   Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
11;   Copyright 2009 Intel Corporation; author: H. Peter Anvin
12;
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.
18;
19; ****************************************************************************
20
21%define IS_PXELINUX 1
22%include "head.inc"
23%include "pxe.inc"
24
25; gPXE extensions support
26%define GPXE	1
27
28;
29; Some semi-configurable constants... change on your own risk.
30;
31my_id		equ pxelinux_id
32NULLFILE	equ 0			; Zero byte == null file name
33NULLOFFSET	equ 0			; Position in which to look
34REBOOT_TIME	equ 5*60		; If failure, time until full reset
35%assign HIGHMEM_SLOP 128*1024		; Avoid this much memory near the top
36TFTP_BLOCKSIZE_LG2 equ 9		; log2(bytes/block)
37TFTP_BLOCKSIZE	equ (1 << TFTP_BLOCKSIZE_LG2)
38
39SECTOR_SHIFT	equ TFTP_BLOCKSIZE_LG2
40SECTOR_SIZE	equ TFTP_BLOCKSIZE
41
42; ---------------------------------------------------------------------------
43;   BEGIN CODE
44; ---------------------------------------------------------------------------
45
46;
47; Memory below this point is reserved for the BIOS and the MBR
48;
49		section .earlybss
50                global trackbuf
51trackbufsize	equ 8192
52trackbuf	resb trackbufsize	; Track buffer goes here
53		; ends at 2800h
54
55		; These fields save information from before the time
56		; .bss is zeroed... must be in .earlybss
57		global InitStack
58InitStack	resd 1
59
60		section .bss16
61		alignb FILENAME_MAX
62PXEStack	resd 1			; Saved stack during PXE call
63
64		alignb 4
65                global DHCPMagic, RebootTime, BIOSName
66RebootTime	resd 1			; Reboot timeout, if set by option
67LocalBootType	resw 1			; Local boot return code
68DHCPMagic	resb 1			; PXELINUX magic flags
69BIOSName	resw 1			; Dummy variable - always 0
70
71		section .text16
72		global StackBuf
73StackBuf	equ STACK_TOP-44	; Base of stack if we use our own
74StackHome	equ StackBuf
75
76		; PXE loads the whole file, but assume it can't be more
77		; than (384-31)K in size.
78MaxLMA		equ 384*1024
79
80;
81; Primary entry point.
82;
83bootsec		equ $
84_start:
85		jmp 0:_start1		; Canonicalize the address and skip
86					; the patch header
87
88;
89; Patch area for adding hardwired DHCP options
90;
91		align 4
92
93hcdhcp_magic	dd 0x2983c8ac		; Magic number
94hcdhcp_len	dd 7*4			; Size of this structure
95hcdhcp_flags	dd 0			; Reserved for the future
96		global bdhcp_len, adhcp_len
97		; Parameters to be parsed before the ones from PXE
98bdhcp_offset	dd 0			; Offset (entered by patcher)
99bdhcp_len	dd 0			; Length (entered by patcher)
100		; Parameters to be parsed *after* the ones from PXE
101adhcp_offset	dd 0			; Offset (entered by patcher)
102adhcp_len	dd 0			; Length (entered by patcher)
103
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
111
112		cld			; Copy upwards
113		xor ax,ax
114		mov ds,ax
115		mov es,ax
116
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
123
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
131
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
136
137		lss esp,[BaseStack]
138		sti			; Stack set up and ready
139
140;
141; Initialize screen (if we're using one)
142;
143%include "init.inc"
144
145;
146; Tell the user we got this far
147;
148		mov si,syslinux_banner
149		call writestr_early
150
151		mov si,copyright_str
152		call writestr_early
153
154;
155; do fs initialize
156;
157	        mov eax,ROOT_FS_OPS
158		xor ebp,ebp
159                pm_call pm_fs_init
160
161		section .rodata
162		alignz 4
163ROOT_FS_OPS:
164                extern pxe_fs_ops
165		dd pxe_fs_ops
166		dd 0
167
168
169		section .text16
170;
171; Initialize the idle mechanism
172;
173		extern reset_idle
174		pm_call reset_idle
175
176;
177; Now we're all set to start with our *real* business.
178;
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...
183;
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.
187;
188
189;
190; Linux kernel loading code is common.  However, we need to define
191; a couple of helper macros...
192;
193
194; Unload PXE stack
195%define HAVE_UNLOAD_PREP
196%macro	UNLOAD_PREP 0
197		pm_call unload_pxe
198%endmacro
199
200;
201; Jump to 32-bit ELF space
202;
203		pm_call load_env32
204		jmp kaboom		; load_env32() shouldn't return. If it does, then kaboom!
205
206print_hello:
207enter_command:
208auto_boot:
209		pm_call hello
210
211;
212; Save hardwired DHCP options.  This is done before the C environment
213; is initialized, so it has to be done in assembly.
214;
215%define MAX_DHCP_OPTS	4096
216		bits 32
217
218		section .savedata
219		global bdhcp_data, adhcp_data
220bdhcp_data:	resb MAX_DHCP_OPTS
221adhcp_data:	resb MAX_DHCP_OPTS
222
223		section .textnr
224pm_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
238
239adhcp_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
252		ret
253
254		bits 16
255
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
261Kernel_EAX	resd 1
262Kernel_SI	resw 1
263
264		section .bss16
265		alignb 4
266ThisKbdTo	resd 1			; Temporary holder for KbdTimeout
267ThisTotalTo	resd 1			; Temporary holder for TotalTimeout
268KernelExtPtr	resw 1			; During search, final null pointer
269FuncFlag	resb 1			; Escape sequences received from keyboard
270KernelType	resb 1			; Kernel type, from vkernel, if known
271		global KernelName
272KernelName	resb FILENAME_MAX	; Mangled name for kernel
273
274		section .text16
275;
276; COM32 vestigial data structure
277;
278%include "com32.inc"
279
280		section .text16
281		global local_boot16:function hidden
282local_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
299		hlt
300
301;
302; kaboom: write a message and bail out.  Wait for quite a while,
303;	  or a user keypress, then do a hard reboot.
304;
305;         Note: use BIOS_timer here; we may not have jiffies set up.
306;
307                global kaboom
308kaboom:
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
342
343;
344; pxenv
345;
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.
351;
352; While we're at it, save and restore all registers.
353;
354                global pxenv
355pxenv:
356		pushfd
357		pushad
358
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
364
365.disable_timer:
366		call bios_timer_cleanup
367
368.store_stack:
369		pushf
370		cli
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
380
381		; Pre-clear the Status field
382		mov word [es:di],cs
383
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
393
394		pushf
395		cli
396		dec word [cs:PXEStackLock]
397		jns .skip2
398		pop bp
399		lss sp,[cs:PXEStack]
400		push bp
401.skip2:
402		popf
403
404		mov bp,sp
405		and ax,ax
406		setnz [bp+32]			; If AX != 0 set CF on return
407
408		; This clobbers the AX return, but we already saved it into
409		; the PXEStatus variable.
410		popad
411
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
417
418.enable_timer:
419		call timer_init
420
421.pop_flags:
422		popfd				; Restore flags (incl. IF, DF)
423		ret
424
425; Must be after function def due to NASM bug
426                global PXEEntry
427PXEEntry	equ pxenv.jump+1
428
429;
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.
434;
435		section .data16
436		alignb 2
437PXEStackLock	dw -1
438
439		section .bss16
440		alignb 2
441PXEStatus	resb 2
442
443		section .text16
444;
445; Invoke INT 1Ah on the PXE stack.  This is used by the "Plan C" method
446; for finding the PXE entry point.
447;
448                global pxe_int1a
449pxe_int1a:
450		mov [cs:PXEStack],sp
451		mov [cs:PXEStack+2],ss
452		lss sp,[cs:InitStack]
453
454		int 1Ah			; May trash registers
455
456		lss sp,[cs:PXEStack]
457		ret
458
459;
460; Special unload for gPXE: this switches the InitStack from
461; gPXE to the ROM PXE stack.
462;
463%if GPXE
464		global gpxe_unload
465gpxe_unload:
466		mov bx,PXENV_FILE_EXIT_HOOK
467		mov di,pxe_file_exit_hook
468		call pxenv
469		jc .plain
470
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:
488		cli
489
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
508
509.plain:
510		ret
511
512writestr_early:
513		pm_call pm_writestr
514		ret
515
516pollchar:
517		pm_call pm_pollchar
518		ret
519
520getchar:
521		pm_call pm_getchar
522		ret
523
524		section .data16
525		alignz 4
526pxe_file_exit_hook:
527.status:	dw 0
528.offset:	dw gpxe_unload.resume
529.seg:		dw 0
530%endif
531
532		section .text16
533
534; -----------------------------------------------------------------------------
535;  PXE modules
536; -----------------------------------------------------------------------------
537
538%if IS_LPXELINUX
539%include "pxeisr.inc"
540%endif
541
542; -----------------------------------------------------------------------------
543;  Common modules
544; -----------------------------------------------------------------------------
545
546%include "common.inc"		; Universal modules
547
548; -----------------------------------------------------------------------------
549;  Begin data section
550; -----------------------------------------------------------------------------
551
552		section .data16
553
554		global copyright_str, syslinux_banner
555copyright_str   db 'Copyright (C) 1994-'
556		asciidec YEAR
557		db ' H. Peter Anvin et al', CR, LF, 0
558err_bootfailed	db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0
559bailmsg		equ err_bootfailed
560localboot_msg	db 'Booting from local disk...', CR, LF, 0
561syslinux_banner	db CR, LF, MY_NAME, ' ', VERSION_STR, ' ', MY_TYPE, ' '
562		db DATE_STR, ' ', 0
563
564;
565; Misc initialized (data) variables
566;
567		section .data16
568                global KeepPXE
569KeepPXE		db 0			; Should PXE be kept around?
570
571		section .bss16
572		global OrigFDCTabPtr
573OrigFDCTabPtr	resd 1			; Keep bios_cleanup_hardware() honest
574