xref: /dragonfly/stand/boot/pc32/pxeldr/pxeldr.S (revision 7d3e9a5b)
1/*
2 * Copyright (c) 2000 John Baldwin
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are freely
6 * permitted provided that the above copyright notice and this
7 * paragraph and the following disclaimer are duplicated in all
8 * such forms.
9 *
10 * This software is provided "AS IS" and without any express or
11 * implied warranties, including, without limitation, the implied
12 * warranties of merchantability and fitness for a particular
13 * purpose.
14 *
15 *
16 * $FreeBSD: src/sys/boot/i386/pxeldr/pxeldr.s,v 1.9 2003/09/03 08:12:20 phk Exp $
17 * $DragonFly: src/sys/boot/pc32/pxeldr/pxeldr.S,v 1.5 2007/05/18 07:41:43 dillon Exp $
18 */
19
20/*
21 * This simple program is a preloader for the normal boot3 loader.  It is
22 * simply prepended to the beginning of a fully built and btxld'd loader.
23 * It then copies the loader to the address boot2 normally loads it,
24 * emulates the boot[12] environment (protected mode, a bootinfo struct,
25 * etc.), and then jumps to the start of btxldr to start the boot process.
26 * This method allows a stock /boot/loader to be booted over the network
27 * via PXE w/o having to write a separate PXE-aware client just to load
28 * the loader.
29 */
30#include "../bootasm.h"
31
32		/*
33		 * a.out header fields
34		 */
35		.set AOUT_TEXT,0x04		# text segment size
36		.set AOUT_DATA,0x08		# data segment size
37		.set AOUT_BSS,0x0c		# zerod BSS size
38		.set AOUT_SYMBOLS,0x10		# symbol table
39		.set AOUT_ENTRY,0x14		# entry point
40		.set AOUT_HEADER,MEM_PAGE_SIZE	# size of the a.out header
41
42		/*
43		 * Flags for kargs->bootflags
44		 */
45		.set KARGS_FLAGS_PXE,0x2	# flag to indicate booting from
46						#  PXE loader
47		/*
48		 * Boot howto bits
49		 */
50		.set RB_SERIAL,0x1000		# serial console
51
52		/*
53		 * Segment selectors.
54		 */
55		.set SEL_SDATA,0x8		# Supervisor data
56		.set SEL_RDATA,0x10		# Real mode data
57		.set SEL_SCODE,0x18		# PM-32 code
58		.set SEL_SCODE16,0x20		# PM-16 code
59
60		/*
61		 * BTX constants
62		 */
63		.set INT_SYS,0x30		# BTX syscall interrupt
64
65		/*
66		 * Bit in BDA_KEYBOARD that is set if an enhanced
67		 * keyboard is present.
68		 */
69		.set KEYBOARD_BIT,0x10
70
71		/*
72		 * We expect to be loaded by the BIOS at LOAD (0x7c00),
73		 * which is the standard boot loader entry point.
74		 */
75		.code16
76		.globl start
77		.org 0x0, 0x0
78
79		/*
80		 * BTX program loader for PXE network booting
81		 */
82start:		cld				# string ops inc
83		xorw %ax, %ax			# zero %ax
84		movw %ax, %ss			# setup the
85		movw $start, %sp		#  stack
86		movw %es, %cx			# save PXENV+ segment
87		movw %ax, %ds			# setup the
88		movw %ax, %es			#  data segments
89		andl $0xffff, %ecx		# clear upper words
90		andl $0xffff, %ebx		#  of %ebx and %ecx
91		shll $4, %ecx			# calculate the offset of
92		addl %ebx, %ecx			#  the PXENV+ struct and
93		pushl %ecx			#  save it on the stack
94		movw $welcome_msg, %si		# %ds:(%si) -> welcome message
95		callw putstr			# display the welcome message
96
97		/*
98		 * Setup the arguments that the loader is expecting
99		 * from boot[12]
100		 */
101		movw $bootinfo_msg, %si		# %ds:(%si) -> boot args message
102		callw putstr			# display the message
103		movw $MEM_ARG, %bx		# %ds:(%bx) -> boot args
104		movw %bx, %di			# %es:(%di) -> boot args
105		xorl %eax, %eax			# zero %eax
106		movw $(MEM_ARG_SIZE/4), %cx	# Size of arguments in 32-bit
107						#  dwords
108		rep				# Clear the arguments
109		stosl				#  to zero
110		orb $KARGS_FLAGS_PXE, 0x8(%bx)	# kargs->bootflags |=
111						#  KARGS_FLAGS_PXE
112		popl 0xc(%bx)			# kargs->pxeinfo = *PXENV+
113#ifdef ALWAYS_SERIAL
114		/*
115		 * set the RBX_SERIAL bit in the howto byte.
116		 */
117		orl $RB_SERIAL, (%bx)		# enable serial console
118#endif
119#ifdef PROBE_KEYBOARD
120		/*
121		 * Look at the BIOS data area to see if we have an enhanced
122		 * keyboard.  If not, set the RBX_SERIAL bit in the howto
123		 * byte.
124		 */
125		testb $KEYBOARD_BIT, BDA_KEYBOARD # keyboard present?
126		jnz keyb			# yes, so skip
127		orl $RB_SERIAL, (%bx)		# enable serial console
128keyb:
129#endif
130		/*
131		 * Turn on the A20 address line
132		 */
133		callw seta20			# Turn A20 on
134
135		/*
136		 * Relocate the loader and BTX using a very lazy protected
137		 * mode
138		 */
139		movw $relocate_msg, %si		# Display the
140		callw putstr			#  relocation message
141		movl end+AOUT_ENTRY, %edi	# %edi is the destination
142		movl $(end+AOUT_HEADER), %esi	# %esi is
143						#  the start of the text
144						#  segment
145		movl end+AOUT_TEXT, %ecx	# %ecx = length of the text
146						#  segment
147		lgdt gdtdesc			# setup our own gdt
148		cli				# turn off interrupts
149		movl %cr0, %eax			# Turn on
150		orb $0x1, %al			#  protected
151		movl %eax, %cr0			#  mode
152		ljmp $SEL_SCODE,$pm_start	# long jump to clear the
153						#  instruction pre-fetch queue
154		.code32
155pm_start:	movw $SEL_SDATA, %ax		# Initialize
156		movw %ax, %ds			#  %ds and
157		movw %ax, %es			#  %es to a flat selector
158		rep				# Relocate the
159		movsb				#  text segment
160		addl $(MEM_PAGE_SIZE - 1), %edi	# pad %edi out to a new page
161		andl $~(MEM_PAGE_SIZE - 1), %edi #  for the data segment
162		movl end+AOUT_DATA, %ecx	# size of the data segment
163		rep				# Relocate the
164		movsb				#  data segment
165		movl end+AOUT_BSS, %ecx		# size of the bss
166		xorl %eax, %eax			# zero %eax
167		addb $3, %cl			# round %ecx up to
168		shrl $2, %ecx			#  a multiple of 4
169		rep				# zero the
170		stosl				#  bss
171		movl end+AOUT_ENTRY, %esi	# %esi -> relocated loader
172		addl $MEM_BTX_LDR_OFF, %esi	# %esi -> BTX in the loader
173		movl $MEM_BTX_ORG, %edi	# %edi -> where BTX needs to go
174		movzwl 0xa(%esi), %ecx		# %ecx -> length of BTX
175		rep				# Relocate
176		movsb				#  BTX
177		ljmp $SEL_SCODE16,$pm_16	# Jump to 16-bit PM
178		.code16
179pm_16:		movw $SEL_RDATA, %ax		# Initialize
180		movw %ax, %ds			#  %ds and
181		movw %ax, %es			#  %es to a real mode selector
182		movl %cr0, %eax			# Turn off
183		andb $~0x1, %al			#  protected
184		movl %eax, %cr0			#  mode
185		ljmp $0,$pm_end			# Long jump to clear the
186						#  instruction pre-fetch queue
187pm_end:		sti				# Turn interrupts back on now
188
189		/*
190		 * Copy the BTX client to MEM_BTX_USR
191		 */
192		xorw %ax, %ax			# zero %ax and set
193		movw %ax, %ds			#  %ds and %es
194		movw %ax, %es			#  to segment 0
195		movw $MEM_BTX_USR, %di		# Prepare to relocate
196		movw $btx_client, %si		#  the simple btx client
197		movw $(btx_client_end-btx_client), %cx # length of btx client
198		rep				# Relocate the
199		movsb				#  simple BTX client
200
201		/*
202		 * Copy the boot[12] args to where the BTX client can
203		 * see them
204		 */
205		movw $MEM_ARG, %si		# where the args are at now
206		movw $MEM_BTX_USR_ARG, %di		# where the args are moving to
207		movw $(MEM_ARG_SIZE/4), %cx	# size of the arguments in longs
208		rep				# Relocate
209		movsl				#  the words
210
211		/*
212		 * Save the entry point so the client can get to it later on
213		 */
214		movl end+AOUT_ENTRY, %eax	# load the entry point
215		stosl				# add it to the end of the
216						#  arguments
217		/*
218		 * Now we just start up BTX and let it do the rest
219		 */
220		movw $jump_message, %si		# Display the
221		callw putstr			#  jump message
222		ljmp $0,$MEM_BTX_ENTRY		# Jump to the BTX entry point
223
224		/*
225		 * Display a null-terminated string
226		 */
227putstr:		lodsb				# load %al from %ds:(%si)
228		testb %al,%al			# stop at null
229		jnz putc			# if the char != null, output it
230		retw				# return when null is hit
231putc:		movw $0x7,%bx			# attribute for output
232		movb $0xe,%ah			# BIOS: put_char
233		int $0x10			# call BIOS, print char in %al
234		jmp putstr			# keep looping
235
236		/*
237		 * Enable A20. Put upper limit on amount of time we wait for the
238		 * keyboard controller to get ready (65K x ISA access time). If
239		 * we wait more than that amount it's likely that the hardware
240		 * is legacy-free and simply doesn't have keyboard controller
241		 * and don't need enabling A20 at all.
242		 */
243seta20: 	cli				# Disable interrupts
244		xor %cx,%cx			# Clear
245seta20.1:	inc %cx				# Increment, overflow?
246		jz seta20.3			# Yes
247		inb $0x64,%al			# Get status
248		testb $0x2,%al			# Busy?
249		jnz seta20.1			# Yes
250		movb $0xd1,%al			# Command: Write
251		outb %al,$0x64			#  output port
252seta20.2:	inb $0x64,%al			# Get status
253		testb $0x2,%al			# Busy?
254		jnz seta20.2			# Yes
255		movb $0xdf,%al			# Enable
256		outb %al,$0x60			#  A20
257seta20.3:	sti				# Enable interrupts
258		retw				# To caller
259
260		/*
261		 * BTX client to start btxldr
262		 */
263		.code32
264btx_client:	movl $(MEM_BTX_USR_ARG-MEM_BTX_USR+MEM_ARG_SIZE-4), %esi
265						# %ds:(%esi) -> end
266						#  of boot[12] args
267		movl $(MEM_ARG_SIZE/4), %ecx	# Number of words to push
268		std				# Go backwards
269push_arg:	lodsl				# Read argument
270		pushl %eax			# Push it onto the stack
271		loop push_arg			# Push all of the arguments
272		cld				# In case anyone depends on this
273		pushl MEM_BTX_USR_ARG-MEM_BTX_USR+MEM_ARG_SIZE # Entry point of
274						#  the loader
275		pushl %eax			# Emulate a near call
276		movl $0x1, %eax			# "exec" system call
277		int $INT_SYS			# BTX system call
278btx_client_end:
279		.code16
280
281		.p2align 4
282
283		/*
284		 * Global descriptor table.
285		 */
286gdt:		.word 0x0,0x0,0x0,0x0		# Null entry
287		.word 0xffff,0x0,0x9200,0xcf	# SEL_SDATA
288		.word 0xffff,0x0,0x9200,0x0	# SEL_RDATA
289		.word 0xffff,0x0,0x9a00,0xcf	# SEL_SCODE (32-bit)
290		.word 0xffff,0x0,0x9a00,0x8f	# SEL_SCODE16 (16-bit)
291gdt.1:
292
293		/*
294		 * Pseudo-descriptors.
295		 */
296gdtdesc:	.word gdt.1-gdt-1		# Limit
297		.long gdt			# Base
298
299welcome_msg:	.asciz	"PXE Loader 1.00\r\n\n"
300bootinfo_msg:	.asciz	"Building the boot loader arguments\r\n"
301relocate_msg:	.asciz	"Relocating the loader and the BTX\r\n"
302jump_message:	.asciz	"Starting the BTX loader\r\n"
303
304		.p2align 4
305end:
306