Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/stand/i386/cdboot/cdboot.S
34878 views
1
#
2
# Copyright (c) 2001 John Baldwin <[email protected]>
3
#
4
# Redistribution and use in source and binary forms, with or without
5
# modification, are permitted provided that the following conditions
6
# are met:
7
# 1. Redistributions of source code must retain the above copyright
8
# notice, this list of conditions and the following disclaimer.
9
# 2. Redistributions in binary form must reproduce the above copyright
10
# notice, this list of conditions and the following disclaimer in the
11
# documentation and/or other materials provided with the distribution.
12
#
13
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23
# SUCH DAMAGE.
24
#
25
26
#
27
# This program is a freestanding boot program to load an a.out binary
28
# from a CD-ROM booted with no emulation mode as described by the El
29
# Torito standard. Due to broken BIOSen that do not load the desired
30
# number of sectors, we try to fit this in as small a space as possible.
31
#
32
# Basically, we first create a set of boot arguments to pass to the loaded
33
# binary. Then we attempt to load /boot/loader from the CD we were booted
34
# off of.
35
#
36
37
#include <bootargs.h>
38
39
#
40
# Memory locations.
41
#
42
.set MEM_PAGE_SIZE,0x1000 # memory page size, 4k
43
.set MEM_ARG,0x900 # Arguments at start
44
.set MEM_ARG_BTX,0xa100 # Where we move them to so the
45
# BTX client can see them
46
.set MEM_ARG_SIZE,0x18 # Size of the arguments
47
.set MEM_BTX_ADDRESS,0x9000 # where BTX lives
48
.set MEM_BTX_ENTRY,0x9010 # where BTX starts to execute
49
.set MEM_BTX_OFFSET,MEM_PAGE_SIZE # offset of BTX in the loader
50
.set MEM_BTX_CLIENT,0xa000 # where BTX clients live
51
#
52
# a.out header fields
53
#
54
.set AOUT_TEXT,0x04 # text segment size
55
.set AOUT_DATA,0x08 # data segment size
56
.set AOUT_BSS,0x0c # zero'd BSS size
57
.set AOUT_SYMBOLS,0x10 # symbol table
58
.set AOUT_ENTRY,0x14 # entry point
59
.set AOUT_HEADER,MEM_PAGE_SIZE # size of the a.out header
60
#
61
# Segment selectors.
62
#
63
.set SEL_SDATA,0x8 # Supervisor data
64
.set SEL_RDATA,0x10 # Real mode data
65
.set SEL_SCODE,0x18 # PM-32 code
66
.set SEL_SCODE16,0x20 # PM-16 code
67
#
68
# BTX constants
69
#
70
.set INT_SYS,0x30 # BTX syscall interrupt
71
#
72
# Constants for reading from the CD.
73
#
74
.set ERROR_TIMEOUT,0x80 # BIOS timeout on read
75
.set NUM_RETRIES,3 # Num times to retry
76
.set SECTOR_SIZE,0x800 # size of a sector
77
.set SECTOR_SHIFT,11 # number of place to shift
78
.set BUFFER_LEN,0x100 # number of sectors in buffer
79
.set MAX_READ,0x10000 # max we can read at a time
80
.set MAX_READ_SEC,MAX_READ >> SECTOR_SHIFT
81
.set MEM_READ_BUFFER,0x9000 # buffer to read from CD
82
.set MEM_VOLDESC,MEM_READ_BUFFER # volume descriptor
83
.set MEM_DIR,MEM_VOLDESC+SECTOR_SIZE # Lookup buffer
84
.set VOLDESC_LBA,0x10 # LBA of vol descriptor
85
.set VD_PRIMARY,1 # Primary VD
86
.set VD_END,255 # VD Terminator
87
.set VD_ROOTDIR,156 # Offset of Root Dir Record
88
.set DIR_LEN,0 # Offset of Dir Record length
89
.set DIR_EA_LEN,1 # Offset of EA length
90
.set DIR_EXTENT,2 # Offset of 64-bit LBA
91
.set DIR_SIZE,10 # Offset of 64-bit length
92
.set DIR_NAMELEN,32 # Offset of 8-bit name len
93
.set DIR_NAME,33 # Offset of dir name
94
#
95
# We expect to be loaded by the BIOS at 0x7c00 (standard boot loader entry
96
# point)
97
#
98
.code16
99
.globl start
100
.org 0x0, 0x0
101
#
102
# Program start.
103
#
104
start: cld # string ops inc
105
xor %ax,%ax # zero %ax
106
mov %ax,%ss # setup the
107
mov $start,%sp # stack
108
mov %ax,%ds # setup the
109
mov %ax,%es # data segments
110
mov %dl,drive # Save BIOS boot device
111
mov $msg_welcome,%si # %ds:(%si) -> welcome message
112
call putstr # display the welcome message
113
#
114
# Setup the arguments that the loader is expecting from boot[12]
115
#
116
mov $msg_bootinfo,%si # %ds:(%si) -> boot args message
117
call putstr # display the message
118
mov $MEM_ARG,%bx # %ds:(%bx) -> boot args
119
mov %bx,%di # %es:(%di) -> boot args
120
xor %eax,%eax # zero %eax
121
mov $(MEM_ARG_SIZE/4),%cx # Size of arguments in 32-bit
122
# dwords
123
rep # Clear the arguments
124
stosl # to zero
125
mov drive,%dl # Store BIOS boot device
126
mov %dl,0x4(%bx) # in kargs->bootdev
127
orb $KARGS_FLAGS_CD,0x8(%bx) # kargs->bootflags |=
128
# KARGS_FLAGS_CD
129
#
130
# Load Volume Descriptor
131
#
132
mov $VOLDESC_LBA,%eax # Set LBA of first VD
133
load_vd: push %eax # Save %eax
134
mov $1,%dh # One sector
135
mov $MEM_VOLDESC,%ebx # Destination
136
call read # Read it in
137
cmpb $VD_PRIMARY,(%bx) # Primary VD?
138
je have_vd # Yes
139
pop %eax # Prepare to
140
inc %eax # try next
141
cmpb $VD_END,(%bx) # Last VD?
142
jne load_vd # No, read next
143
mov $msg_novd,%si # No VD
144
jmp error # Halt
145
have_vd: # Have Primary VD
146
#
147
# Try to look up the loader binary using the paths in the loader_paths
148
# array.
149
#
150
mov $loader_paths,%si # Point to start of array
151
lookup_path: push %si # Save file name pointer
152
call lookup # Try to find file
153
pop %di # Restore file name pointer
154
jnc lookup_found # Found this file
155
xor %al,%al # Look for next
156
mov $0xffff,%cx # path name by
157
repnz # scanning for
158
scasb # nul char
159
mov %di,%si # Point %si at next path
160
mov (%si),%al # Get first char of next path
161
or %al,%al # Is it double nul?
162
jnz lookup_path # No, try it.
163
mov $msg_failed,%si # Failed message
164
jmp error # Halt
165
lookup_found: # Found a loader file
166
#
167
# Load the binary into the buffer. Due to real mode addressing limitations
168
# we have to read it in 64k chunks.
169
#
170
mov DIR_SIZE(%bx),%eax # Read file length
171
add $SECTOR_SIZE-1,%eax # Convert length to sectors
172
shr $SECTOR_SHIFT,%eax
173
cmp $BUFFER_LEN,%eax
174
jbe load_sizeok
175
mov $msg_load2big,%si # Error message
176
call error
177
load_sizeok: movzbw %al,%cx # Num sectors to read
178
mov DIR_EXTENT(%bx),%eax # Load extent
179
xor %edx,%edx
180
mov DIR_EA_LEN(%bx),%dl
181
add %edx,%eax # Skip extended
182
mov $MEM_READ_BUFFER,%ebx # Read into the buffer
183
load_loop: mov %cl,%dh
184
cmp $MAX_READ_SEC,%cl # Truncate to max read size
185
jbe load_notrunc
186
mov $MAX_READ_SEC,%dh
187
load_notrunc: sub %dh,%cl # Update count
188
push %eax # Save
189
call read # Read it in
190
pop %eax # Restore
191
add $MAX_READ_SEC,%eax # Update LBA
192
add $MAX_READ,%ebx # Update dest addr
193
jcxz load_done # Done?
194
jmp load_loop # Keep going
195
load_done:
196
#
197
# Turn on the A20 address line
198
#
199
call seta20 # Turn A20 on
200
#
201
# Relocate the loader and BTX using a very lazy protected mode
202
#
203
mov $msg_relocate,%si # Display the
204
call putstr # relocation message
205
mov MEM_READ_BUFFER+AOUT_ENTRY,%edi # %edi is the destination
206
mov $(MEM_READ_BUFFER+AOUT_HEADER),%esi # %esi is
207
# the start of the text
208
# segment
209
mov MEM_READ_BUFFER+AOUT_TEXT,%ecx # %ecx = length of the text
210
# segment
211
push %edi # Save entry point for later
212
lgdt gdtdesc # setup our own gdt
213
cli # turn off interrupts
214
mov %cr0,%eax # Turn on
215
or $0x1,%al # protected
216
mov %eax,%cr0 # mode
217
ljmp $SEL_SCODE,$pm_start # long jump to clear the
218
# instruction pre-fetch queue
219
.code32
220
pm_start: mov $SEL_SDATA,%ax # Initialize
221
mov %ax,%ds # %ds and
222
mov %ax,%es # %es to a flat selector
223
rep # Relocate the
224
movsb # text segment
225
add $(MEM_PAGE_SIZE - 1),%edi # pad %edi out to a new page
226
and $~(MEM_PAGE_SIZE - 1),%edi # for the data segment
227
mov MEM_READ_BUFFER+AOUT_DATA,%ecx # size of the data segment
228
rep # Relocate the
229
movsb # data segment
230
mov MEM_READ_BUFFER+AOUT_BSS,%ecx # size of the bss
231
xor %eax,%eax # zero %eax
232
add $3,%cl # round %ecx up to
233
shr $2,%ecx # a multiple of 4
234
rep # zero the
235
stosl # bss
236
mov MEM_READ_BUFFER+AOUT_ENTRY,%esi # %esi -> relocated loader
237
add $MEM_BTX_OFFSET,%esi # %esi -> BTX in the loader
238
mov $MEM_BTX_ADDRESS,%edi # %edi -> where BTX needs to go
239
movzwl 0xa(%esi),%ecx # %ecx -> length of BTX
240
rep # Relocate
241
movsb # BTX
242
ljmp $SEL_SCODE16,$pm_16 # Jump to 16-bit PM
243
.code16
244
pm_16: mov $SEL_RDATA,%ax # Initialize
245
mov %ax,%ds # %ds and
246
mov %ax,%es # %es to a real mode selector
247
mov %cr0,%eax # Turn off
248
and $~0x1,%al # protected
249
mov %eax,%cr0 # mode
250
ljmp $0,$pm_end # Long jump to clear the
251
# instruction pre-fetch queue
252
pm_end: sti # Turn interrupts back on now
253
#
254
# Copy the BTX client to MEM_BTX_CLIENT
255
#
256
xor %ax,%ax # zero %ax and set
257
mov %ax,%ds # %ds and %es
258
mov %ax,%es # to segment 0
259
mov $MEM_BTX_CLIENT,%di # Prepare to relocate
260
mov $btx_client,%si # the simple btx client
261
mov $(btx_client_end-btx_client),%cx # length of btx client
262
rep # Relocate the
263
movsb # simple BTX client
264
#
265
# Copy the boot[12] args to where the BTX client can see them
266
#
267
mov $MEM_ARG,%si # where the args are at now
268
mov $MEM_ARG_BTX,%di # where the args are moving to
269
mov $(MEM_ARG_SIZE/4),%cx # size of the arguments in longs
270
rep # Relocate
271
movsl # the words
272
#
273
# Save the entry point so the client can get to it later on
274
#
275
pop %eax # Restore saved entry point
276
stosl # and add it to the end of
277
# the arguments
278
#
279
# Now we just start up BTX and let it do the rest
280
#
281
mov $msg_jump,%si # Display the
282
call putstr # jump message
283
ljmp $0,$MEM_BTX_ENTRY # Jump to the BTX entry point
284
285
#
286
# Lookup the file in the path at [SI] from the root directory.
287
#
288
# Trashes: All but BX
289
# Returns: CF = 0 (success), BX = pointer to record
290
# CF = 1 (not found)
291
#
292
lookup: mov $VD_ROOTDIR+MEM_VOLDESC,%bx # Root directory record
293
push %si
294
mov $msg_lookup,%si # Display lookup message
295
call putstr
296
pop %si
297
push %si
298
call putstr
299
mov $msg_lookup2,%si
300
call putstr
301
pop %si
302
lookup_dir: lodsb # Get first char of path
303
cmp $0,%al # Are we done?
304
je lookup_done # Yes
305
cmp $'/',%al # Skip path separator.
306
je lookup_dir
307
dec %si # Undo lodsb side effect
308
call find_file # Lookup first path item
309
jnc lookup_dir # Try next component
310
mov $msg_lookupfail,%si # Not found message
311
call putstr
312
stc # Set carry
313
ret
314
jmp error
315
lookup_done: mov $msg_lookupok,%si # Success message
316
call putstr
317
clc # Clear carry
318
ret
319
320
#
321
# Lookup file at [SI] in directory whose record is at [BX].
322
#
323
# Trashes: All but returns
324
# Returns: CF = 0 (success), BX = pointer to record, SI = next path item
325
# CF = 1 (not found), SI = preserved
326
#
327
find_file: mov DIR_EXTENT(%bx),%eax # Load extent
328
xor %edx,%edx
329
mov DIR_EA_LEN(%bx),%dl
330
add %edx,%eax # Skip extended attributes
331
mov %eax,rec_lba # Save LBA
332
mov DIR_SIZE(%bx),%eax # Save size
333
mov %eax,rec_size
334
xor %cl,%cl # Zero length
335
push %si # Save
336
ff.namelen: inc %cl # Update length
337
lodsb # Read char
338
cmp $0,%al # Nul?
339
je ff.namedone # Yes
340
cmp $'/',%al # Path separator?
341
jnz ff.namelen # No, keep going
342
ff.namedone: dec %cl # Adjust length and save
343
mov %cl,name_len
344
pop %si # Restore
345
ff.load: mov rec_lba,%eax # Load LBA
346
mov $MEM_DIR,%ebx # Address buffer
347
mov $1,%dh # One sector
348
call read # Read directory block
349
incl rec_lba # Update LBA to next block
350
ff.scan: mov %ebx,%edx # Check for EOF
351
sub $MEM_DIR,%edx
352
cmp %edx,rec_size
353
ja ff.scan.1
354
stc # EOF reached
355
ret
356
ff.scan.1: cmpb $0,DIR_LEN(%bx) # Last record in block?
357
je ff.nextblock
358
push %si # Save
359
movzbw DIR_NAMELEN(%bx),%si # Find end of string
360
ff.checkver: cmpb $'0',DIR_NAME-1(%bx,%si) # Less than '0'?
361
jb ff.checkver.1
362
cmpb $'9',DIR_NAME-1(%bx,%si) # Greater than '9'?
363
ja ff.checkver.1
364
dec %si # Next char
365
jnz ff.checkver
366
jmp ff.checklen # All numbers in name, so
367
# no version
368
ff.checkver.1: movzbw DIR_NAMELEN(%bx),%cx
369
cmp %cx,%si # Did we find any digits?
370
je ff.checkdot # No
371
cmpb $';',DIR_NAME-1(%bx,%si) # Check for semicolon
372
jne ff.checkver.2
373
dec %si # Skip semicolon
374
mov %si,%cx
375
mov %cl,DIR_NAMELEN(%bx) # Adjust length
376
jmp ff.checkdot
377
ff.checkver.2: mov %cx,%si # Restore %si to end of string
378
ff.checkdot: cmpb $'.',DIR_NAME-1(%bx,%si) # Trailing dot?
379
jne ff.checklen # No
380
decb DIR_NAMELEN(%bx) # Adjust length
381
ff.checklen: pop %si # Restore
382
movzbw name_len,%cx # Load length of name
383
cmp %cl,DIR_NAMELEN(%bx) # Does length match?
384
je ff.checkname # Yes, check name
385
ff.nextrec: add DIR_LEN(%bx),%bl # Next record
386
adc $0,%bh
387
jmp ff.scan
388
ff.nextblock: subl $SECTOR_SIZE,rec_size # Adjust size
389
jnc ff.load # If subtract ok, keep going
390
ret # End of file, so not found
391
ff.checkname: lea DIR_NAME(%bx),%di # Address name in record
392
push %si # Save
393
repe cmpsb # Compare name
394
je ff.match # We have a winner!
395
pop %si # Restore
396
jmp ff.nextrec # Keep looking.
397
ff.match: add $2,%sp # Discard saved %si
398
clc # Clear carry
399
ret
400
401
#
402
# Load DH sectors starting at LBA EAX into [EBX].
403
#
404
# Trashes: EAX
405
#
406
read: push %si # Save
407
push %cx # Save since some BIOSs trash
408
mov %eax,edd_lba # LBA to read from
409
mov %ebx,%eax # Convert address
410
shr $4,%eax # to segment
411
mov %ax,edd_addr+0x2 # and store
412
read.retry: call twiddle # Entertain the user
413
push %dx # Save
414
mov $edd_packet,%si # Address Packet
415
mov %dh,edd_len # Set length
416
mov drive,%dl # BIOS Device
417
mov $0x42,%ah # BIOS: Extended Read
418
int $0x13 # Call BIOS
419
pop %dx # Restore
420
jc read.fail # Worked?
421
pop %cx # Restore
422
pop %si
423
ret # Return
424
read.fail: cmp $ERROR_TIMEOUT,%ah # Timeout?
425
je read.retry # Yes, Retry.
426
read.error: mov %ah,%al # Save error
427
mov $hex_error,%di # Format it
428
call hex8 # as hex
429
mov $msg_badread,%si # Display Read error message
430
431
#
432
# Display error message at [SI] and halt.
433
#
434
error: call putstr # Display message
435
halt: hlt
436
jmp halt # Spin
437
438
#
439
# Display a null-terminated string.
440
#
441
# Trashes: AX, SI
442
#
443
putstr: push %bx # Save
444
putstr.load: lodsb # load %al from %ds:(%si)
445
test %al,%al # stop at null
446
jnz putstr.putc # if the char != null, output it
447
pop %bx # Restore
448
ret # return when null is hit
449
putstr.putc: call putc # output char
450
jmp putstr.load # next char
451
452
#
453
# Display a single char.
454
#
455
putc: mov $0x7,%bx # attribute for output
456
mov $0xe,%ah # BIOS: put_char
457
int $0x10 # call BIOS, print char in %al
458
ret # Return to caller
459
460
#
461
# Output the "twiddle"
462
#
463
twiddle: push %ax # Save
464
push %bx # Save
465
mov twiddle_index,%al # Load index
466
mov $twiddle_chars,%bx # Address table
467
inc %al # Next
468
and $3,%al # char
469
mov %al,twiddle_index # Save index for next call
470
xlat # Get char
471
call putc # Output it
472
mov $8,%al # Backspace
473
call putc # Output it
474
pop %bx # Restore
475
pop %ax # Restore
476
ret
477
478
#
479
# Enable A20. Put an upper limit on the amount of time we wait for the
480
# keyboard controller to get ready (65K x ISA access time). If
481
# we wait more than that amount, the hardware is probably
482
# legacy-free and simply doesn't have a keyboard controller.
483
# Thus, the A20 line is already enabled.
484
#
485
seta20: cli # Disable interrupts
486
xor %cx,%cx # Clear
487
seta20.1: inc %cx # Increment, overflow?
488
jz seta20.3 # Yes
489
in $0x64,%al # Get status
490
test $0x2,%al # Busy?
491
jnz seta20.1 # Yes
492
mov $0xd1,%al # Command: Write
493
out %al,$0x64 # output port
494
seta20.2: in $0x64,%al # Get status
495
test $0x2,%al # Busy?
496
jnz seta20.2 # Yes
497
mov $0xdf,%al # Enable
498
out %al,$0x60 # A20
499
seta20.3: sti # Enable interrupts
500
ret # To caller
501
502
#
503
# Convert AL to hex, saving the result to [EDI].
504
#
505
hex8: pushl %eax # Save
506
shrb $0x4,%al # Do upper
507
call hex8.1 # 4
508
popl %eax # Restore
509
hex8.1: andb $0xf,%al # Get lower 4
510
cmpb $0xa,%al # Convert
511
sbbb $0x69,%al # to hex
512
das # digit
513
orb $0x20,%al # To lower case
514
stosb # Save char
515
ret # (Recursive)
516
517
#
518
# BTX client to start btxldr
519
#
520
.code32
521
btx_client: mov $(MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE-4), %esi
522
# %ds:(%esi) -> end
523
# of boot[12] args
524
mov $(MEM_ARG_SIZE/4),%ecx # Number of words to push
525
std # Go backwards
526
push_arg: lodsl # Read argument
527
push %eax # Push it onto the stack
528
loop push_arg # Push all of the arguments
529
cld # In case anyone depends on this
530
pushl MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE # Entry point of
531
# the loader
532
push %eax # Emulate a near call
533
mov $0x1,%eax # 'exec' system call
534
int $INT_SYS # BTX system call
535
btx_client_end:
536
.code16
537
538
.p2align 4
539
#
540
# Global descriptor table.
541
#
542
gdt: .word 0x0,0x0,0x0,0x0 # Null entry
543
.word 0xffff,0x0,0x9200,0xcf # SEL_SDATA
544
.word 0xffff,0x0,0x9200,0x0 # SEL_RDATA
545
.word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE (32-bit)
546
.word 0xffff,0x0,0x9a00,0x8f # SEL_SCODE16 (16-bit)
547
gdt.1:
548
#
549
# Pseudo-descriptors.
550
#
551
gdtdesc: .word gdt.1-gdt-1 # Limit
552
.long gdt # Base
553
#
554
# EDD Packet
555
#
556
edd_packet: .byte 0x10 # Length
557
.byte 0 # Reserved
558
edd_len: .byte 0x0 # Num to read
559
.byte 0 # Reserved
560
edd_addr: .word 0x0,0x0 # Seg:Off
561
edd_lba: .quad 0x0 # LBA
562
563
drive: .byte 0
564
565
#
566
# State for searching dir
567
#
568
rec_lba: .long 0x0 # LBA (adjusted for EA)
569
rec_size: .long 0x0 # File size
570
name_len: .byte 0x0 # Length of current name
571
572
twiddle_index: .byte 0x0
573
574
msg_welcome: .asciz "CD Loader 1.2\r\n\n"
575
msg_bootinfo: .asciz "Building the boot loader arguments\r\n"
576
msg_relocate: .asciz "Relocating the loader and the BTX\r\n"
577
msg_jump: .asciz "Starting the BTX loader\r\n"
578
msg_badread: .ascii "Read Error: 0x"
579
hex_error: .asciz "00\r\n"
580
msg_novd: .asciz "Could not find Primary Volume Descriptor\r\n"
581
msg_lookup: .asciz "Looking up "
582
msg_lookup2: .asciz "... "
583
msg_lookupok: .asciz "Found\r\n"
584
msg_lookupfail: .asciz "File not found\r\n"
585
msg_load2big: .asciz "File too big\r\n"
586
msg_failed: .asciz "Boot failed\r\n"
587
twiddle_chars: .ascii "|/-\\"
588
loader_paths: .asciz "/BOOT/LOADER"
589
.asciz "/boot/loader"
590
.byte 0
591
592
593