MAJOR BOOT CODE REVAMP / 30 hour+ hacking session (50 if you include the
[dragonfly.git] / sys / boot / pc32 / btx / btxldr / btxldr.S
1 /*
2  * Copyright (c) 2003,2004 The DragonFly Project.  All rights reserved.
3  * 
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  * 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  * 
34  * Copyright (c) 1998 Robert Nordier
35  * All rights reserved.
36  *
37  * Redistribution and use in source and binary forms are freely
38  * permitted provided that the above copyright notice and this
39  * paragraph and the following disclaimer are duplicated in all
40  * such forms.
41  *
42  * This software is provided "AS IS" and without any express or
43  * implied warranties, including, without limitation, the implied
44  * warranties of merchantability and fitness for a particular
45  * purpose.
46  *
47  * $FreeBSD: src/sys/boot/i386/btx/btxldr/Makefile,v 1.17 2004/04/27 19:45:16 ru Exp $
48  * $DragonFly: src/sys/boot/pc32/btx/btxldr/btxldr.S,v 1.3 2004/07/19 23:30:34 dillon Exp $
49  */
50
51 /*
52  * Prototype BTX loader program, written in a couple of hours.  The
53  * real thing should probably be more flexible, and in C.
54  */
55
56 #include "../../bootasm.h"
57 /*
58  * Memory locations.
59  */
60                 .set MEM_DATA,start+0x1000      # Data segment
61 /*
62  * Segment selectors.
63  */
64                 .set SEL_SCODE,0x8              # 4GB code
65                 .set SEL_SDATA,0x10             # 4GB data
66                 .set SEL_RCODE,0x18             # 64K code
67                 .set SEL_RDATA,0x20             # 64K data
68 /*
69  * Paging constants.
70  */
71                 .set PAG_SIZ,0x1000             # Page size
72                 .set PAG_ENT,0x4                # Page entry size
73 /*
74  * Screen constants.
75  */
76                 .set SCR_MAT,0x7                # Mode/attribute
77                 .set SCR_COL,0x50               # Columns per row
78                 .set SCR_ROW,0x19               # Rows per screen
79 /*
80  * Required by aout gas inadequacy.
81  */
82                 .set SIZ_STUB,0x1a              # Size of stub
83 /*
84  * We expect to be loaded by boot2 at the origin defined in ./Makefile.
85  * This is typically 0x200000.
86  *
87  * I *THINK* (not sure) that execution begins with us in 'virtual mode',
88  * meaning everything is offset by MEM_BTX_USR.  We will load a gdt to
89  * set the base offsets back to 0.
90  */
91                 .globl start
92 /*
93  * BTX program loader for ELF clients.
94  */
95 start:          cld                             # String ops inc
96                 movl $m_logo,%esi               # Identify
97                 call putstr                     #  ourselves
98 #if !defined(MEM_BTX_USR_STK)
99                 movzwl BDA_MEM,%eax             # Get base memory
100                 shll $0xa,%eax                  # Convert to bytes
101 #else
102                 movl $MEM_BTX_USR_STK,%eax
103 #endif
104                 movl %eax,%ebp                  # Base of user stack
105 #ifdef BTXLDR_VERBOSE
106                 movl $m_mem,%esi                # Display
107                 call hexout                     #  amount of
108                 call putstr                     #  base memory
109 #endif
110
111                 /*
112                  * Load a new GDT.  XXX what does this do to running code
113                  * segments?  What if an interrupt occurs?  What if the 
114                  * segment registers are reloaded?
115                  */
116                 lgdt gdtdesc
117
118                 /*
119                  * Relocate caller's arguments.
120                  */
121 #ifdef BTXLDR_VERBOSE
122                 movl $m_esp,%esi                # Display
123                 movl %esp,%eax                  #  caller
124                 call hexout                     #  stack
125                 call putstr                     #  pointer
126                 movl $m_args,%esi               # Format string
127                 leal 0x4(%esp,1),%ebx           # First argument
128                 movl $0x6,%ecx                  # Count
129 start.1:        movl (%ebx),%eax                # Get argument and
130                 addl $0x4,%ebx                  #  bump pointer
131                 call hexout                     # Display it
132                 loop start.1                    # Till done
133                 call putstr                     # End message
134 #endif
135                 /*
136                  * Arguments: (entry, boothowto, bootdev, 0, 0, 0, bootinfo)
137                  *              0x00,  0x04,      0x08,             0x18
138                  *
139                  * sizeof(bootinfo) == 0x48 (BOOTINFO_SIZE)
140                  * sizeof arguments == 0x18 (MEM_ARG_SIZE)
141                  * total arguments  == 0x60 bytes (USR_ARGOFFSET)
142                  */
143
144                 movl $BOOTINFO_SIZE,%ecx        # Allocate space
145                 subl %ecx,%ebp                  #  for bootinfo
146                 movl 0x18(%esp,1),%esi          # Source: bootinfo
147                 cmpl $0x0, %esi                 # If the bootinfo pointer
148                 je start_null_bi                #  is null, do not copy it
149                 movl %ebp,%edi                  # Destination
150                 rep                             # Copy
151                 movsb                           #  it
152                 movl %ebp,0x18(%esp,1)          # Update pointer
153 #ifdef BTXLDR_VERBOSE
154                 movl $m_rel_bi,%esi             # Display
155                 movl %ebp,%eax                  #  bootinfo
156                 call hexout                     #  relocation
157                 call putstr                     #  message
158 #endif
159 start_null_bi:  movl $0x18,%ecx                 # Allocate space
160                 subl %ecx,%ebp                  #  for arguments
161                 leal 0x4(%esp,1),%esi           # Source
162                 movl %ebp,%edi                  # Destination
163                 rep                             # Copy
164                 movsb                           #  them
165 #ifdef BTXLDR_VERBOSE
166                 movl $m_rel_args,%esi           # Display
167                 movl %ebp,%eax                  #  argument
168                 call hexout                     #  relocation
169                 call putstr                     #  message
170 #endif
171 /*
172  * Set up BTX kernel.
173  */
174                 movl $MEM_BTX_ESP,%esp          # Set up new stack
175                 movl $MEM_DATA,%ebx             # Data segment
176                 movl $m_vers,%esi               # Display BTX
177                 call putstr                     #  version message
178                 movb 0x5(%ebx),%al              # Get major version
179                 addb $'0',%al                   # Display
180                 call putchr                     #  it
181                 movb $'.',%al                   # And a
182                 call putchr                     #  dot
183                 movb 0x6(%ebx),%al              # Get minor
184                 xorb %ah,%ah                    #  version
185                 movb $0xa,%dl                   # Divide
186                 divb %dl,%al                    #  by 10
187                 addb $'0',%al                   # Display
188                 call putchr                     #  tens
189                 movb %ah,%al                    # Get units
190                 addb $'0',%al                   # Display
191                 call putchr                     #  units
192                 call putstr                     # End message
193
194                 # Relocate the BTX image from wherever it was loaded (%ebx),
195                 # which is typically offset 0x1000 in the load data, to
196                 # MEM_BTX_ORG (typically 0x9000).
197                 #
198                 # MEM_BTX_TBL + ((mappages | 0x3ff) + 1) * 4
199                 # mappages is typically 0x0ffn so we get 0x1000*4 = 0x4000
200                 # MEM_BTX_TBL is traditionally mapped at 0x5000 so the
201                 # whole calculation translated to MEM_BTX_ORG (0x9000).
202 #if 0
203                 /* XXX what is all of this junk? */
204                 movzwl 0x8(%ebx),%edi           # Compute the BTX load address
205                 orl $PAG_SIZ/PAG_ENT-1,%edi     # (by skipping the page table)
206                 incl %edi
207                 shll $0x2,%edi
208                 addl $MEM_BTX_TBL,%edi
209 #else
210                 movl $MEM_BTX_ORG,%edi
211 #endif
212                 movl %ebx,%esi                  # %esi = BTX image source
213                 pushl %edi                      # Save load address
214                 movzwl 0xa(%ebx),%ecx           # Image size (bytes)
215 #ifdef BTXLDR_VERBOSE
216                 pushl %ecx                      # Save image size
217 #endif
218                 rep                             # Relocate BTX
219                 movsb
220                 movl %esi,%ebx                  # Keep place
221 #ifdef BTXLDR_VERBOSE
222                 movl $m_rel_btx,%esi            # Restore
223                 popl %eax                       #  parameters
224                 call hexout                     #  and
225 #endif
226                 popl %ebp                       #  display
227 #ifdef BTXLDR_VERBOSE
228                 movl %ebp,%eax                  #  the
229                 call hexout                     #  relocation
230                 call putstr                     #  message
231 #endif
232                 /*
233                  * ADJUST EBP FOR USER BASE ADDRESS
234                  *
235                  * XXX why not just move MEM_BTX_USR into %ebp ?
236                  */
237                 addl $MEM_BTX_USR-MEM_BTX_ORG,%ebp
238 #ifdef BTXLDR_VERBOSE
239                 movl $m_base,%esi               #  the
240                 movl %ebp,%eax                  #  user
241                 call hexout                     #  base
242                 call putstr                     #  address
243 #endif
244 /*
245  * Set up ELF-format client program.
246  */
247                 cmpl $0x464c457f,(%ebx)         # ELF magic number?
248                 je start.3                      # Yes
249                 movl $e_fmt,%esi                # Display error
250                 call putstr                     #  message
251 start.2:        jmp start.2                     # Hang
252 start.3:
253 #ifdef BTXLDR_VERBOSE
254                 movl $m_elf,%esi                # Display ELF
255                 call putstr                     #  message
256                 movl $m_segs,%esi               # Format string
257 #endif
258                 movl $0x2,%edi                  # Segment count
259                 movl 0x1c(%ebx),%edx            # Get e_phoff
260                 addl %ebx,%edx                  # To pointer
261                 movzwl 0x2c(%ebx),%ecx          # Get e_phnum
262 start.4:        cmpl $0x1,(%edx)                # Is p_type PT_LOAD?
263                 jne start.6                     # No
264 #ifdef BTXLDR_VERBOSE
265                 movl 0x4(%edx),%eax             # Display
266                 call hexout                     #  p_offset
267                 movl 0x8(%edx),%eax             # Display
268                 call hexout                     #  p_vaddr
269                 movl 0x10(%edx),%eax            # Display
270                 call hexout                     #  p_filesz
271                 movl 0x14(%edx),%eax            # Display
272                 call hexout                     #  p_memsz
273                 call putstr                     # End message
274 #endif
275                 pushl %esi                      # Save
276                 pushl %edi                      #  working
277                 pushl %ecx                      #  registers
278                 movl 0x4(%edx),%esi             # Get p_offset
279                 addl %ebx,%esi                  #  as pointer
280                 movl 0x8(%edx),%edi             # Get p_vaddr
281                 addl %ebp,%edi                  #  as pointer
282                 movl 0x10(%edx),%ecx            # Get p_filesz
283                 rep                             # Set up
284                 movsb                           #  segment
285                 movl 0x14(%edx),%ecx            # Any bytes
286                 subl 0x10(%edx),%ecx            #  to zero?
287                 jz start.5                      # No
288                 xorb %al,%al                    # Then
289                 rep                             #  zero
290                 stosb                           #  them
291 start.5:        popl %ecx                       # Restore
292                 popl %edi                       #  working
293                 popl %esi                       #  registers
294                 decl %edi                       # Segments to do
295                 je start.7                      # If none
296 start.6:        addl $0x20,%edx                 # To next entry
297                 loop start.4                    # Till done
298 start.7:
299 #ifdef BTXLDR_VERBOSE
300                 movl $m_done,%esi               # Display done
301                 call putstr                     #  message
302 #endif
303                 movl $start.8,%esi              # Real mode stub
304                 movl $BOOT0_ORIGIN,%edi         # Destination
305                 movl $start.9-start.8,%ecx      # Size
306                 rep                             # Relocate
307                 movsb                           #  it
308                 ljmp $SEL_RCODE,$BOOT0_ORIGIN   # To 16-bit code
309                 .code16
310 start.8:        xorw %ax,%ax                    # Data
311                 movb $SEL_RDATA,%al             #  selector
312                 movw %ax,%ss                    # Reload SS
313                 movw %ax,%ds                    # Reset
314                 movw %ax,%es                    #  other
315                 movw %ax,%fs                    #  segment
316                 movw %ax,%gs                    #  limits
317                 movl %cr0,%eax                  # Switch to
318                 decw %ax                        #  real
319                 movl %eax,%cr0                  #  mode
320                 ljmp $0,$MEM_BTX_ENTRY          # Jump to BTX entry point
321 start.9:
322                 .code32
323 /*
324  * Output message [ESI] followed by EAX in hex.
325  */
326 hexout:         pushl %eax                      # Save
327                 call putstr                     # Display message
328                 popl %eax                       # Restore
329                 pushl %esi                      # Save
330                 pushl %edi                      # callers
331                 movl $buf,%edi                  # Buffer
332                 pushl %edi                      # Save
333                 call hex32                      # To hex
334                 xorb %al,%al                    # Terminate
335                 stosb                           #  string
336                 popl %esi                       # Restore
337 hexout.1:       lodsb                           # Get a char
338                 cmpb $'0',%al                   # Leading zero?
339                 je hexout.1                     # Yes
340                 testb %al,%al                   # End of string?
341                 jne hexout.2                    # No
342                 decl %esi                       # Undo
343 hexout.2:       decl %esi                       # Adjust for inc
344                 call putstr                     # Display hex
345                 popl %edi                       # Restore
346                 popl %esi                       # callers
347                 ret                             # To caller
348 /*
349  * Output zero-terminated string [ESI] to the console.
350  */
351 putstr.0:       call putchr                     # Output char
352 putstr:         lodsb                           # Load char
353                 testb %al,%al                   # End of string?
354                 jne putstr.0                    # No
355                 ret                             # To caller
356 /*
357  * Output character AL to the console.
358  */
359 putchr:         pusha                           # Save
360                 xorl %ecx,%ecx                  # Zero for loops
361                 movb $SCR_MAT,%ah               # Mode/attribute
362                 movl $BDA_POS,%ebx              # BDA pointer
363                 movw (%ebx),%dx                 # Cursor position
364                 movl $0xb8000,%edi              # Regen buffer (color)
365                 cmpb %ah,BDA_SCR-BDA_POS(%ebx)  # Mono mode?
366                 jne putchr.1                    # No
367                 xorw %di,%di                    # Regen buffer (mono)
368 putchr.1:       cmpb $0xa,%al                   # New line?
369                 je putchr.2                     # Yes
370                 xchgl %eax,%ecx                 # Save char
371                 movb $SCR_COL,%al               # Columns per row
372                 mulb %dh                        #  * row position
373                 addb %dl,%al                    #  + column
374                 adcb $0x0,%ah                   #  position
375                 shll %eax                       #  * 2
376                 xchgl %eax,%ecx                 # Swap char, offset
377                 movw %ax,(%edi,%ecx,1)          # Write attr:char
378                 incl %edx                       # Bump cursor
379                 cmpb $SCR_COL,%dl               # Beyond row?
380                 jb putchr.3                     # No
381 putchr.2:       xorb %dl,%dl                    # Zero column
382                 incb %dh                        # Bump row
383 putchr.3:       cmpb $SCR_ROW,%dh               # Beyond screen?
384                 jb putchr.4                     # No
385                 leal 2*SCR_COL(%edi),%esi       # New top line
386                 movw $(SCR_ROW-1)*SCR_COL/2,%cx # Words to move
387                 rep                             # Scroll
388                 movsl                           #  screen
389                 movb $' ',%al                   # Space
390                 movb $SCR_COL,%cl               # Columns to clear
391                 rep                             # Clear
392                 stosw                           #  line
393                 movb $SCR_ROW-1,%dh             # Bottom line
394 putchr.4:       movw %dx,(%ebx)                 # Update position
395                 popa                            # Restore
396                 ret                             # To caller
397 /*
398  * Convert EAX, AX, or AL to hex, saving the result to [EDI].
399  */
400 hex32:          pushl %eax                      # Save
401                 shrl $0x10,%eax                 # Do upper
402                 call hex16                      #  16
403                 popl %eax                       # Restore
404 hex16:          call hex16.1                    # Do upper 8
405 hex16.1:        xchgb %ah,%al                   # Save/restore
406 hex8:           pushl %eax                      # Save
407                 shrb $0x4,%al                   # Do upper
408                 call hex8.1                     #  4
409                 popl %eax                       # Restore
410 hex8.1:         andb $0xf,%al                   # Get lower 4
411                 cmpb $0xa,%al                   # Convert
412                 sbbb $0x69,%al                  #  to hex
413                 das                             #  digit
414                 orb $0x20,%al                   # To lower case
415                 stosb                           # Save char
416                 ret                             # (Recursive)
417
418                 .data
419                 .p2align 4
420 /*
421  * Global descriptor table.
422  */
423 gdt:            .word 0x0,0x0,0x0,0x0           # Null entry
424                 .word 0xffff,0x0,0x9a00,0xcf    # SEL_SCODE
425                 .word 0xffff,0x0,0x9200,0xcf    # SEL_SDATA
426                 .word 0xffff,0x0,0x9a00,0x0     # SEL_RCODE
427                 .word 0xffff,0x0,0x9200,0x0     # SEL_RDATA
428 gdt.1:
429 gdtdesc:        .word gdt.1-gdt-1               # Limit
430                 .long gdt                       # Base
431 /*
432  * Messages.
433  */
434 m_logo:         .asciz " \nBTX loader 1.00  "
435 m_vers:         .asciz "BTX version is \0\n"
436 e_fmt:          .asciz "Error: Client format not supported\n"
437 #ifdef BTXLDR_VERBOSE
438 m_mem:          .asciz "Starting in protected mode (base mem=\0)\n"
439 m_esp:          .asciz "Arguments passed (esp=\0):\n"
440 m_args:         .asciz"<howto="
441                 .asciz" bootdev="
442                 .asciz" junk="
443                 .asciz" "
444                 .asciz" "
445                 .asciz" bootinfo=\0>\n"
446 m_rel_bi:       .asciz "Relocated bootinfo (size=48) to \0\n"
447 m_rel_args:     .asciz "Relocated arguments (size=18) to \0\n"
448 m_rel_btx:      .asciz "Relocated kernel (size=\0) to \0\n"
449 m_base:         .asciz "Client base address is \0\n"
450 m_elf:          .asciz "Client format is ELF\n"
451 m_segs:         .asciz "text segment: offset="
452                 .asciz " vaddr="
453                 .asciz " filesz="
454                 .asciz " memsz=\0\n"
455                 .asciz "data segment: offset="
456                 .asciz " vaddr="
457                 .asciz " filesz="
458                 .asciz " memsz=\0\n"
459 m_done:         .asciz "Loading complete\n"
460 #endif
461 /*
462  * Uninitialized data area.
463  */
464 buf:                                            # Scratch buffer