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