1/*
| 1/*
|
| 2 * Copyright (c) 2008 Luigi Rizzo (mostly documentation)
|
2 * Copyright (c) 2002 Bruce M. Simpson 3 * Copyright (c) 1998 Robert Nordier 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms are freely 7 * permitted provided that the above copyright notice and this 8 * paragraph and the following disclaimer are duplicated in all 9 * such forms. 10 * 11 * This software is provided "AS IS" and without any express or 12 * implied warranties, including, without limitation, the implied 13 * warranties of merchantability and fitness for a particular 14 * purpose. 15 *
| 3 * Copyright (c) 2002 Bruce M. Simpson 4 * Copyright (c) 1998 Robert Nordier 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms are freely 8 * permitted provided that the above copyright notice and this 9 * paragraph and the following disclaimer are duplicated in all 10 * such forms. 11 * 12 * This software is provided "AS IS" and without any express or 13 * implied warranties, including, without limitation, the implied 14 * warranties of merchantability and fitness for a particular 15 * purpose. 16 *
|
16 * $FreeBSD: head/sys/boot/i386/boot0/boot0.S 185346 2008-11-26 21:38:43Z luigi $
| 17 * $FreeBSD: head/sys/boot/i386/boot0/boot0.S 185562 2008-12-02 14:57:48Z luigi $
|
17 */ 18
| 18 */ 19
|
19/* A 512-byte boot manager. */ 20#ifdef SIO 21/* ... using a serial console on COM1. */
| 20/* build options: */ 21#ifdef SIO /* use serial console on COM1. */
|
22#endif 23
| 22#endif 23
|
| 24#ifdef PXE /* enable PXE/INT18 booting with F6 */ 25#endif 26 27#ifdef CHECK_DRIVE /* make sure we boot from a HD. */ 28#endif 29 30#ifdef ONLY_F_KEYS /* Only F1..F6, no digits on console */ 31#endif 32 33#ifdef TEST /* enable some test code */ 34#ifndef ONLY_F_KEYS 35#define ONLY_F_KEYS /* make room for the test code */ 36#endif 37#endif 38 39/* 40 * Note - this code uses many tricks to save space and fit in one sector. 41 * This includes using side effects of certain instructions, reusing 42 * register values from previous operations, etc. 43 * Be extremely careful when changing the code, even for simple things. 44 */ 45 46/* 47 * BOOT BLOCK STRUCTURE 48 * 49 * This code implements a Master Boot Record (MBR) for an Intel/PC disk. 50 * It is 512 bytes long and it is normally loaded by the BIOS (or another 51 * bootloader) at 0:0x7c00. This code depends on %cs:%ip being 0:0x7c00 52 * 53 * The initial chunk of instructions is used as a signature by external 54 * tools (e.g. boot0cfg) which can manipulate the block itself. 55 * 56 * The area at offset 0x1b2 contains a magic string ('Drive '), also 57 * used as a signature to detect the block, and some variables that can 58 * be updated by boot0cfg (and optionally written back to the disk). 59 * These variables control the operation of the bootloader itself, 60 * e.g. which partitions to enable, the timeout, the use of LBA 61 * (called 'packet') or CHS mode, whether to force a drive number, 62 * and whether to write back the user's selection back to disk. 63 * 64 * As in every Master Boot Record, the partition table is at 0x1be, 65 * made of four 16-byte entries each containing: 66 * 67 * OFF SIZE DESCRIPTION 68 * 0 1 status (0x80: bootable, 0: non bootable) 69 * 1 3 start sector CHS 70 * 8:head, 6:sector, 2:cyl bit 9..8, 8:cyl bit 7..0 71 * 4 1 partition type 72 * 5 3 end sector CHS 73 * 8 4 LBA of first sector 74 * 12 4 partition size in sectors 75 * 76 * and followed by the two bytes 0x55, 0xAA (MBR signature). 77 */ 78 79 80/* 81 * BOOT BLOCK OPERATION 82 * 83 * On entry, the registers contain the following values: 84 * 85 * %cs:%ip 0:0x7c00 86 * %dl drive number (0x80, 0x81, ... ) 87 * %si pointer to the partition table from which we were loaded. 88 * Some boot code (e.g. syslinux) use this info to relocate 89 * themselves, so we want to pass a valid one to the next stage. 90 * NOTE: the use of %is is not a standard. 91 * 92 * This boot block first relocates itself at a different address (0:0x600), 93 * to free the space at 0:0x7c00 for the next stage boot block. 94 * 95 * It then initializes some memory at 0:0x800 and above (pointed by %bp) 96 * to store the original drive number (%dl) passed to us, and to construct a 97 * fake partition entry. The latter is used by the disk I/O routine and, 98 * in some cases, passed in %si to the next stage boot code. 99 * 100 * The variables at 0x1b2 are accessed as negative offsets from %bp. 101 * 102 * After the relocation, the code scans the partition table printing 103 * out enabled partition or disks, and waits for user input. 104 * 105 * When a partition is selected, or a timeout expires, the currently 106 * selected partition is used to load the next stage boot code, 107 * %dl and %si are set appropriately as when we were called, and 108 * control is transferred to the newly loaded code at 0:0x7c00. 109 */ 110 111/* 112 * CONSTANTS 113 * 114 * NHRDRV is the address in segment 0 where the BIOS writes the 115 * total number of hard disks in the system. 116 * LOAD is the original load address and cannot be changed. 117 * ORIGIN is the relocation address. If you change it, you also need 118 * to change the value passed to the linker in the Makefile 119 * PRT_OFF is the location of the partition table (from the MBR standard). 120 * B0_OFF is the location of the data area, known to boot0cfg so 121 * it cannot be changed. 122 * MAGIC is the signature of a boot block. 123 */ 124
|
24 .set NHRDRV,0x475 # Number of hard drives 25 .set ORIGIN,0x600 # Execution address
| 125 .set NHRDRV,0x475 # Number of hard drives 126 .set ORIGIN,0x600 # Execution address
|
26 .set FAKE,0x800 # Partition entry
| |
27 .set LOAD,0x7c00 # Load address 28 29 .set PRT_OFF,0x1be # Partition table
| 127 .set LOAD,0x7c00 # Load address 128 129 .set PRT_OFF,0x1be # Partition table
|
| 130 .set B0_OFF,0x1b2 # Offset of boot0 data
|
30
| 131
|
31 .set TBL0SZ,0x3 # Table 0 size 32 .set TBL1SZ,0xa # Table 1 size 33
| |
34 .set MAGIC,0xaa55 # Magic: bootable
| 132 .set MAGIC,0xaa55 # Magic: bootable
|
35 .set B0MAGIC,0xbb66 # Identification
| |
36 37 .set KEY_ENTER,0x1c # Enter key scan code 38 .set KEY_F1,0x3b # F1 key scan code 39 .set KEY_1,0x02 # #1 key scan code 40
| 133 134 .set KEY_ENTER,0x1c # Enter key scan code 135 .set KEY_F1,0x3b # F1 key scan code 136 .set KEY_1,0x02 # #1 key scan code 137
|
41 .set ASCII_BEL,0x07 # ASCII code for <BEL>
| 138 .set ASCII_BEL,'#' # ASCII code for <BEL>
|
42 .set ASCII_CR,0x0D # ASCII code for <CR> 43 44/*
| 139 .set ASCII_CR,0x0D # ASCII code for <CR> 140 141/*
|
45 * Addresses in the sector of embedded data values. 46 * Accessed with negative offsets from the end of the relocated sector (%ebp).
| 142 * Offsets of variables in the block at B0_OFF, and in the volatile 143 * data area, computed as displacement from %bp. 144 * We need to define them as constant as the assembler cannot 145 * compute them in its single pass.
|
47 */
| 146 */
|
48 .set _NXTDRV,-0x48 # Next drive 49 .set _OPT,-0x47 # Default option 50 .set _SETDRV,-0x46 # Drive to force 51 .set _FLAGS,-0x45 # Flags 52 .set _TICKS,-0x44 # Timeout ticks 53 .set _FAKE,0x0 # Fake partition entry 54 .set _MNUOPT,0xc # Menu options
| 147 .set _NXTDRV, -0x48 # Next drive 148 .set _OPT, -0x47 # Default option 149 .set _SETDRV, -0x46 # Drive to force 150 .set _FLAGS, -0x45 # Flags 151 .set SETDRV, 0x20 # the 'setdrv' flag 152 .set NOUPDATE, 0x40 # the 'noupdate' flag 153 .set USEPACKET, 0x80 # the 'packet' flag 154 .set _TICKS, -0x44 # Timeout ticks 155 .set _FAKE,0x0 # Fake partition table 156 .set _MNUOPT, 0x10 # Saved menu entries
|
55
| 157
|
| 158 .set TLEN, (desc_ofs - bootable_ids) # size of bootable ids
|
56 .globl start # Entry point 57 .code16 # This runs in real mode 58 59/*
| 159 .globl start # Entry point 160 .code16 # This runs in real mode 161 162/*
|
| 163 * MAIN ENTRY POINT
|
60 * Initialise segments and registers to known values. 61 * segments start at 0. 62 * The stack is immediately below the address we were loaded to.
| 164 * Initialise segments and registers to known values. 165 * segments start at 0. 166 * The stack is immediately below the address we were loaded to.
|
| 167 * NOTE: the initial section of the code (up to movw $LOAD,%sp) 168 * is used by boot0cfg, together with the 'Drive ' string and 169 * the 0x55, 0xaa at the end, as an identifier for version 1.0 170 * of the boot code. Do not change it. 171 * In version 1.0 the parameter table (_NEXTDRV etc) is at 0x1b9
|
63 */ 64start: cld # String ops inc 65 xorw %ax,%ax # Zero 66 movw %ax,%es # Address 67 movw %ax,%ds # data 68 movw %ax,%ss # Set up 69 movw $LOAD,%sp # stack 70
| 172 */ 173start: cld # String ops inc 174 xorw %ax,%ax # Zero 175 movw %ax,%es # Address 176 movw %ax,%ds # data 177 movw %ax,%ss # Set up 178 movw $LOAD,%sp # stack 179
|
71/* 72 * Copy this code to the address it was linked for 73 */
| 180 /* 181 * Copy this code to the address it was linked for, 0x600 by default. 182 */
|
74 movw %sp,%si # Source 75 movw $start,%di # Destination 76 movw $0x100,%cx # Word count 77 rep # Relocate 78 movsw # code
| 183 movw %sp,%si # Source 184 movw $start,%di # Destination 185 movw $0x100,%cx # Word count 186 rep # Relocate 187 movsw # code
|
79/* 80 * Set address for variable space beyond code, and clear it. 81 * Notice that this is also used to point to the values embedded in the block, 82 * by using negative offsets. 83 */
| 188 /* 189 * After the code, (i.e. at %di+0, 0x800) create a partition entry, 190 * initialized to LBA 0 / CHS 0:0:1. 191 * Set %bp to point to the partition and also, with negative offsets, 192 * to the variables embedded in the bootblock (nextdrv and so on). 193 */
|
84 movw %di,%bp # Address variables 85 movb $0x8,%cl # Words to clear 86 rep # Zero 87 stosw # them
| 194 movw %di,%bp # Address variables 195 movb $0x8,%cl # Words to clear 196 rep # Zero 197 stosw # them
|
88/* 89 * Relocate to the new copy of the code. 90 */ 91 incb -0xe(%di) # Sector number 92 jmp main-LOAD+ORIGIN # To relocated code
| 198 incb -0xe(%di) # Set the S field to 1
|
93
| 199
|
| 200 jmp main-LOAD+ORIGIN # Jump to relocated code 201
|
94main: 95#if defined(SIO) && COMSPEED != 0
| 202main: 203#if defined(SIO) && COMSPEED != 0
|
96/* 97 * Initialize the serial port. bioscom preserves the driver number in DX. 98 */
| 204 /* 205 * Init the serial port. bioscom preserves the driver number in DX. 206 */
|
99 movw $COMSPEED,%ax # defined by Makefile 100 callw bioscom 101#endif
| 207 movw $COMSPEED,%ax # defined by Makefile 208 callw bioscom 209#endif
|
102/* 103 * Check what flags were loaded with us, specifically if a predefined drive 104 * number should be used. If what the bios gives us is bad, use the '0' in 105 * the block instead. 106 */ 107 testb $0x20,_FLAGS(%bp) # Set drive number? 108 jnz main.1 # Yes
| 210 211 /* 212 * If the 'setdrv' flag is set in the boot sector, use the drive 213 * number from the boot sector at 'setdrv_num'. 214 * Optionally, do the same if the BIOS gives us an invalid number 215 * (note though that the override prevents booting from a floppy 216 * or a ZIP/flash drive in floppy emulation). 217 * The test costs 4 bytes of code so it is disabled by default. 218 */ 219 testb $SETDRV,_FLAGS(%bp) # Set drive number? 220#ifndef CHECK_DRIVE /* disable drive checks */ 221 jz save_curdrive # no, use the default 222#else 223 jnz disable_update # Yes
|
109 testb %dl,%dl # Drive number valid?
| 224 testb %dl,%dl # Drive number valid?
|
110 js main.2 # Possibly (0x80 set) 111/* 112 * Only update the boot-sector when there is a valid drive number or 113 * the drive number is set manually. 114 */ 115 orb $0x40,_FLAGS(%bp) # Disable updates 116main.1: movb _SETDRV(%bp),%dl # Drive number to use 117/* 118 * Whatever we decided to use, now store it into the fake 119 * partition entry that lives in the data space above us. 120 */ 121main.2: movb %dl,_FAKE(%bp) # Save drive number 122 callw putn # To new line 123 pushw %dx # Save drive number 124/* 125 * Start out with a pointer to the 4th byte of the first table entry 126 * so that after 4 iterations it's beyond the end of the sector 127 * and beyond a 256 byte boundary and has overflowed 8 bits (see next comment). 128 * Remember that the table starts 2 bytes earlier than you would expect 129 * as the bootable flag is after it in the block. 130 */
| 225 js save_curdrive # Possibly (0x80 set) 226#endif 227 /* 228 * Disable updates if the drive number is forced. 229 */ 230disable_update: orb $NOUPDATE,_FLAGS(%bp) # Disable updates 231 movb _SETDRV(%bp),%dl # Use stored drive number 232 233 /* 234 * Whatever drive we decided to use, store it at (%bp). The byte 235 * is normally used for the state of the partition (0x80 or 0x00), 236 * but we abuse it as it is very convenient to access at offset 0. 237 * The value is read back after 'check_selection' 238 */ 239save_curdrive: movb %dl, (%bp) # Save drive number 240 pushw %dx # Also in the stack 241#ifdef TEST /* test code, print internal bios drive */ 242 rolb $1, %dl 243 movw $drive, %si 244 call putkey 245#endif 246 callw putn # Print a newline 247 /* 248 * Start out with a pointer to the 4th byte of the first table entry 249 * so that after 4 iterations it's beyond the end of the sector 250 * and beyond a 256 byte boundary. We use the latter trick to check for 251 * end of the loop without using an extra register (see start.5). 252 */
|
131 movw $(partbl+0x4),%bx # Partition table (+4) 132 xorw %dx,%dx # Item number
| 253 movw $(partbl+0x4),%bx # Partition table (+4) 254 xorw %dx,%dx # Item number
|
133/* 134 * Loop around on the partition table, printing values until we 135 * pass a 256 byte boundary. The end of loop test is at main.5. 136 */ 137main.3: movb %ch,-0x4(%bx) # Zero active flag (ch == 0)
| 255 256 /* 257 * Loop around on the partition table, printing values until we 258 * pass a 256 byte boundary. 259 */ 260read_entry: movb %ch,-0x4(%bx) # Zero active flag (ch == 0)
|
138 btw %dx,_FLAGS(%bp) # Entry enabled?
| 261 btw %dx,_FLAGS(%bp) # Entry enabled?
|
139 jnc main.5 # No 140/* 141 * If any of the entries in the table are the same as the 'type' in the slice 142 * table entry, then this is an empty or non bootable partition. Skip it. 143 */
| 262 jnc next_entry # No 263 /* 264 * Lookup type in the 'non_bootable_ids' table, skip matching entries. 265 * This is implemented is by setting %di to the start of the 266 * exclude table, and %cl to the length of the table itself. After the 267 * 'repne scasb' the zero flag is set if we found a match. 268 * If not, %di points to the beginning of the 'valid' types, 269 * which is what we need for the next check. 270 */
|
144 movb (%bx),%al # Load type
| 271 movb (%bx),%al # Load type
|
145 movw $tables,%di # Lookup tables 146 movb $TBL0SZ,%cl # Number of entries
| 272 movw $non_bootable_ids,%di # Lookup tables 273 movb $(bootable_ids-non_bootable_ids),%cl # length
|
147 repne # Exclude 148 scasb # partition?
| 274 repne # Exclude 275 scasb # partition?
|
149 je main.5 # Yes 150/* 151 * Now scan the table of known types 152 */ 153 movb $TBL1SZ+1,%cl # Number of entries
| 276 je next_entry # Yes, ignore it 277 /* 278 * Now scan the table of bootable ids, which starts at %di and has 279 * length TLEN. On a match, %di points to the element following the 280 * match; the corresponding offset to the description is $(TLEN-1) 281 * bytes ahead. If we don't find a match, we hit the 'unknown' entry. 282 */ 283 movb $(TLEN),%cl # Number of entries
|
154 repne # Locate 155 scasb # type
| 284 repne # Locate 285 scasb # type
|
156/* 157 * Get the matching element in the next array. 158 */ 159 addw $TBL1SZ-1, %di # Adjust
| 286 /* 287 * Get the matching element in the next array. 288 * The byte at $(TLEN-1)(%di) contains the offset of the description 289 * string from %di, so we add the number and print the string. 290 */ 291 addw $(TLEN-1), %di # Adjust
|
160 movb (%di),%cl # Partition 161 addw %cx,%di # description 162 callw putx # Display it
| 292 movb (%di),%cl # Partition 293 addw %cx,%di # description 294 callw putx # Display it
|
163main.5: incw %dx # Next item
| 295 296next_entry: incw %dx # Next item
|
164 addb $0x10,%bl # Next entry
| 297 addb $0x10,%bl # Next entry
|
165 jnc main.3 # Till done 166/* 167 * Passed a 256 byte boundary; the table is finished. 168 * Add one to the drive number and check it is valid. 169 */
| 298 jnc read_entry # Till done 299 /* 300 * We are past a 256 byte boundary: the partition table is finished. 301 * Add one to the drive number and check it is valid. 302 * Note that if we started from a floppy, %dl was 0 so we still 303 * get an entry for the next drive, which is the first Hard Disk. 304 */
|
170 popw %ax # Drive number 171 subb $0x80-0x1,%al # Does next 172 cmpb NHRDRV,%al # drive exist? (from BIOS?)
| 305 popw %ax # Drive number 306 subb $0x80-0x1,%al # Does next 307 cmpb NHRDRV,%al # drive exist? (from BIOS?)
|
173 jb main.6 # Yes 174/* 175 * If this is the only drive, don't display it as an option. 176 */
| 308 jb print_drive # Yes 309 /* 310 * If this is the only drive, don't display it as an option. 311 */
|
177 decw %ax # Already drive 0?
| 312 decw %ax # Already drive 0?
|
178 jz main.7 # Yes 179/* 180 * If it was illegal or we cycled through them, go back to drive 0. 181 */
| 313 jz print_prompt # Yes 314 /* 315 * If it was illegal or we cycled through them, go back to drive 0. 316 */
|
182 xorb %al,%al # Drive 0
| 317 xorb %al,%al # Drive 0
|
183/* 184 * Whatever drive we selected, make it an ascii digit and save it back to the 185 * "next drive" location in the loaded block in case we want to save it later 186 * for next time. This also is part of the printed drive string so add 0x80 187 * to indicate end of string. 188 */ 189main.6: addb $'0'|0x80,%al # Save next
| 318 /* 319 * Whatever drive we selected, make it an ascii digit and save it 320 * back to the "nxtdrv" location in case we want to save it to disk. 321 * This digit is also part of the printed drive string, so add 0x80 322 * to indicate end of string. 323 */ 324print_drive: addb $'0'|0x80,%al # Save next
|
190 movb %al,_NXTDRV(%bp) # drive number 191 movw $drive,%di # Display 192 callw putx # item
| 325 movb %al,_NXTDRV(%bp) # drive number 326 movw $drive,%di # Display 327 callw putx # item
|
193/* 194 * Now that we've printed the drive (if we needed to), display a prompt. 195 */ 196main.7: movw $prompt,%si # Display
| 328 /* 329 * Menu is complete, display a prompt followed by current selection. 330 * 'decw %si' makes the register point to the space after 'Default: ' 331 * so we do not see an extra CRLF on the screen. 332 */ 333print_prompt: movw $prompt,%si # Display
|
197 callw putstr # prompt 198 movb _OPT(%bp),%dl # Display 199 decw %si # default 200 callw putkey # key
| 334 callw putstr # prompt 335 movb _OPT(%bp),%dl # Display 336 decw %si # default 337 callw putkey # key
|
201 jmp main.7_1 # Skip beep
| 338 jmp start_input # Skip beep 339
|
202/*
| 340/*
|
203 * Users's last try was bad, beep in displeasure.
| 341 * Here we have the code waiting for user input or a timeout.
|
204 */
| 342 */
|
205main.10: movb $ASCII_BEL,%al # Signal 206 callw putchr # beep! 207/* 208 * Start of input loop. Take note of time 209 */ 210main.7_1: xorb %ah,%ah # BIOS: Get
| 343beep: movb $ASCII_BEL,%al # Input error, print or beep 344 callw putchr 345 346start_input: 347 /* 348 * Actual Start of input loop. Take note of time 349 */ 350 xorb %ah,%ah # BIOS: Get
|
211 int $0x1a # system time 212 movw %dx,%di # Ticks when 213 addw _TICKS(%bp),%di # timeout
| 351 int $0x1a # system time 352 movw %dx,%di # Ticks when 353 addw _TICKS(%bp),%di # timeout
|
214/* 215 * Busy loop, looking for keystrokes but keeping one eye on the time. 216 */ 217main.8:
| 354read_key: 355 /* 356 * Busy loop, looking for keystrokes but keeping one eye on the time. 357 */
|
218#ifndef SIO 219 movb $0x1,%ah # BIOS: Check 220 int $0x16 # for keypress
| 358#ifndef SIO 359 movb $0x1,%ah # BIOS: Check 360 int $0x16 # for keypress
|
221 jnz main.11 # Have one
| |
222#else /* SIO */ 223 movb $0x03,%ah # BIOS: Read COM 224 call bioscom 225 testb $0x01,%ah # Check line status
| 361#else /* SIO */ 362 movb $0x03,%ah # BIOS: Read COM 363 call bioscom 364 testb $0x01,%ah # Check line status
|
226 jnz main.11 # (bit 1 indicates input)
| 365 # (bit 1 indicates input)
|
227#endif /* SIO */
| 366#endif /* SIO */
|
228 xorb %ah,%ah # BIOS: Get 229 int $0x1a # system time
| 367 jnz got_key # Have input 368 xorb %ah,%ah # BIOS: int 0x1a, 00 369 int $0x1a # get system time
|
230 cmpw %di,%dx # Timeout?
| 370 cmpw %di,%dx # Timeout?
|
231 jb main.8 # No 232/* 233 * If timed out or defaulting, come here. 234 */ 235main.9: movb _OPT(%bp),%al # Load default 236 jmp main.12 # Join common code 237/* 238 * Get the keystroke. 239 */ 240main.11:
| 371 jb read_key # No 372 373 /* 374 * Timed out or default selection 375 */ 376use_default: movb _OPT(%bp),%al # Load default 377 jmp check_selection # Join common code 378 379 /* 380 * Get the keystroke. 381 * ENTER or CR confirm the current selection (same as a timeout). 382 * Otherwise convert F1..F6 (or '1'..'6') to 0..5 and check if the 383 * selection is valid. 384 * The SIO code uses ascii chars, the console code uses scancodes. 385 */ 386got_key:
|
241#ifndef SIO
| 387#ifndef SIO
|
242 xorb %ah,%ah # BIOS: Get 243 int $0x16 # keypress 244 movb %ah,%al # Scan code
| 388 xorb %ah,%ah # BIOS: int 0x16, 00 389 int $0x16 # get keypress 390 movb %ah,%al # move scan code to %al 391 cmpb $KEY_ENTER,%al
|
245#else 246 movb $0x02,%ah # BIOS: Receive 247 call bioscom
| 392#else 393 movb $0x02,%ah # BIOS: Receive 394 call bioscom
|
| 395 cmpb $ASCII_CR,%al
|
248#endif
| 396#endif
|
249/* 250 * If it's CR act as if timed out. 251 */ 252#ifndef SIO 253 cmpb $KEY_ENTER,%al # Enter pressed? 254#else 255 cmpb $ASCII_CR,%al # Enter pressed? 256#endif 257 je main.9 # Yes 258/* 259 * Otherwise check if legal. If not ask again. 260 */ 261#ifndef SIO 262 subb $KEY_F1,%al # Less F1 scan code 263 cmpb $0x4,%al # F1..F5? 264 jna main.12 # Yes
| 397 je use_default # enter -> default 398 /* 399 * Check if the key is acceptable, and loop back if not. 400 * The console (non-SIO) code looks at scancodes and accepts 401 * both F1..F6 and 1..6 (the latter costs 6 bytes of code), 402 * relying on the fact that F1..F6 have higher scancodes than 1..6 403 * The SIO code only takes 1..6 404 */ 405#ifdef SIO /* SIO mode, use ascii values */ 406 subb $'1',%al # Subtract '1' ascii code 407#else /* console mode -- use scancodes */ 408 subb $KEY_F1,%al /* Subtract F1 scan code */ 409#if !defined(ONLY_F_KEYS) 410 cmpb $0x5,%al # F1..F6 411 jna 3f # Yes
|
265 subb $(KEY_1 - KEY_F1),%al # Less #1 scan code
| 412 subb $(KEY_1 - KEY_F1),%al # Less #1 scan code
|
266#else 267 subb $'1',%al # Less '1' ascii character 268#endif 269 cmpb $0x4,%al # #1..#5? 270 ja main.10 # No 271/* 272 * We have a selection. If it's a bad selection go back to complain. 273 * The bits in MNUOPT were set when the options were printed. 274 * Anything not printed is not an option. 275 */ 276main.12: cbtw # Option 277 btw %ax,_MNUOPT(%bp) # enabled? 278 jnc main.10 # No 279/* 280 * Save the info in the original tables 281 * for rewriting to the disk. 282 */
| 413 3: 414#endif /* ONLY_F_KEYS */ 415#endif /* SIO */ 416 cmpb $0x5,%al # F1..F6 or 1..6 ? 417#ifdef PXE /* enable PXE/INT18 using F6 */ 418 jne 1f; 419 int $0x18 # found F6, try INT18 420 1: 421#endif /* PXE */ 422 jae beep # Not in F1..F5, beep 423 424check_selection: 425 /* 426 * We have a selection. If it's a bad selection go back to complain. 427 * The bits in MNUOPT were set when the options were printed. 428 * Anything not printed is not an option. 429 */ 430 cbtw # Extend (%ah=0 used later) 431 btw %ax,_MNUOPT(%bp) # Option enabled? 432 jnc beep # No 433 /* 434 * Save the info in the original tables 435 * for rewriting to the disk. 436 */
|
283 movb %al,_OPT(%bp) # Save option
| 437 movb %al,_OPT(%bp) # Save option
|
284 movw $FAKE,%si # Partition for write 285 movb (%si),%dl # Drive number
| 438 439 /* 440 * Make %si and %bx point to the fake partition at LBA 0 (CHS 0:0:1). 441 * Because the correct address is already in %bp, just use it. 442 * Set %dl with the drive number saved in byte 0. 443 * If we have pressed F5 or 5, then this is a good, fake value 444 * to present to the next stage boot code. 445 */ 446 movw %bp,%si # Partition for write 447 movb (%si),%dl # Drive number, saved above
|
286 movw %si,%bx # Partition for read 287 cmpb $0x4,%al # F5/#5 pressed?
| 448 movw %si,%bx # Partition for read 449 cmpb $0x4,%al # F5/#5 pressed?
|
288 pushf # Save 289 je main.13 # Yes
| 450 pushf # Save results for later 451 je 1f # Yes, F5 452 453 /* 454 * F1..F4 was pressed, so make %bx point to the currently 455 * selected partition, and leave the drive number unchanged. 456 */
|
290 shlb $0x4,%al # Point to 291 addw $partbl,%ax # selected 292 xchgw %bx,%ax # partition 293 movb $0x80,(%bx) # Flag active
| 457 shlb $0x4,%al # Point to 458 addw $partbl,%ax # selected 459 xchgw %bx,%ax # partition 460 movb $0x80,(%bx) # Flag active
|
294/* 295 * If not asked to do a write-back (flags 0x40) don't do one. 296 */ 297main.13: pushw %bx # Save 298 testb $0x40,_FLAGS(%bp) # No updates? 299 jnz main.14 # Yes
| 461 /* 462 * If not asked to do a write-back (flags 0x40) don't do one. 463 * Around the call, save the partition pointer to %bx and 464 * restore to %si which is where the next stage expects it. 465 */ 466 1: pushw %bx # Save 467 testb $NOUPDATE,_FLAGS(%bp) # No updates? 468 jnz 2f # skip update
|
300 movw $start,%bx # Data to write 301 movb $0x3,%ah # Write sector 302 callw intx13 # to disk
| 469 movw $start,%bx # Data to write 470 movb $0x3,%ah # Write sector 471 callw intx13 # to disk
|
303main.14: popw %si # Restore 304 popf # Restore 305/* 306 * If going to next drive, replace drive with selected one. 307 * Remember to un-ascii it. Hey 0x80 is already set, cool! 308 */ 309 jne main.15 # If not F5/#5
| 472 2: popw %si # Restore 473 474 /* 475 * If going to next drive, replace drive with selected one. 476 * Remember to un-ascii it. Hey 0x80 is already set, cool! 477 */ 478 popf # Restore %al test results 479 jne 3f # If not F5/#5
|
310 movb _NXTDRV(%bp),%dl # Next drive 311 subb $'0',%dl # number
| 480 movb _NXTDRV(%bp),%dl # Next drive 481 subb $'0',%dl # number
|
312/* 313 * Load selected bootsector to the LOAD location in RAM. 314 * If it fails to read or isn't marked bootable, treat it as a bad selection. 315 */ 316main.15: movw $LOAD,%bx # Address for read
| 482 /* 483 * Load selected bootsector to the LOAD location in RAM. If read 484 * fails or there is no 0x55aa marker, treat it as a bad selection. 485 */ 486 3: movw $LOAD,%bx # Address for read
|
317 movb $0x2,%ah # Read sector 318 callw intx13 # from disk
| 487 movb $0x2,%ah # Read sector 488 callw intx13 # from disk
|
319 jc main.10 # If error
| 489 jc beep # If error
|
320 cmpw $MAGIC,0x1fe(%bx) # Bootable?
| 490 cmpw $MAGIC,0x1fe(%bx) # Bootable?
|
321 jne main.10 # No
| 491 jne beep # No
|
322 pushw %si # Save ptr to selected part. 323 callw putn # Leave some space 324 popw %si # Restore, next stage uses it 325 jmp *%bx # Invoke bootstrap 326 327/* 328 * Display routines
| 492 pushw %si # Save ptr to selected part. 493 callw putn # Leave some space 494 popw %si # Restore, next stage uses it 495 jmp *%bx # Invoke bootstrap 496 497/* 498 * Display routines
|
| 499 * putkey prints the option selected in %dl (F1..F5 or 1..5) followed by 500 * the string at %si 501 * putx: print the option in %dl followed by the string at %di 502 * also record the drive as valid. 503 * puts: print the string at %si followed by a crlf 504 * putn: print a crlf 505 * putstr: print the string at %si 506 * putchr: print the char in al
|
329 */ 330putkey: 331#ifndef SIO 332 movb $'F',%al # Display 333 callw putchr # 'F' 334#endif 335 movb $'1',%al # Prepare 336 addb %dl,%al # digit 337 jmp putstr.1 # Display the rest 338 339/*
| 507 */ 508putkey: 509#ifndef SIO 510 movb $'F',%al # Display 511 callw putchr # 'F' 512#endif 513 movb $'1',%al # Prepare 514 addb %dl,%al # digit 515 jmp putstr.1 # Display the rest 516 517/*
|
340 * Display the option and note that it is a valid option. 341 * That last point is a bit tricky..
| 518 * Display the option and record the drive as valid in the options. 519 * That last point is done using the btsw instruction which does 520 * a test and set. We don't care for the test part.
|
342 */ 343putx: btsw %dx,_MNUOPT(%bp) # Enable menu option 344 movw $item,%si # Display 345 callw putkey # key 346 movw %di,%si # Display the rest 347 348puts: callw putstr # Display string 349 350putn: movw $crlf,%si # To next line 351 352putstr: lodsb # Get byte 353 testb $0x80,%al # End of string? 354 jnz putstr.2 # Yes 355putstr.1: callw putchr # Display char 356 jmp putstr # Continue 357putstr.2: andb $~0x80,%al # Clear MSB 358
| 521 */ 522putx: btsw %dx,_MNUOPT(%bp) # Enable menu option 523 movw $item,%si # Display 524 callw putkey # key 525 movw %di,%si # Display the rest 526 527puts: callw putstr # Display string 528 529putn: movw $crlf,%si # To next line 530 531putstr: lodsb # Get byte 532 testb $0x80,%al # End of string? 533 jnz putstr.2 # Yes 534putstr.1: callw putchr # Display char 535 jmp putstr # Continue 536putstr.2: andb $~0x80,%al # Clear MSB 537
|
359#ifndef SIO
| |
360putchr:
| 538putchr:
|
| 539#ifndef SIO
|
361 pushw %bx # Save 362 movw $0x7,%bx # Page:attribute 363 movb $0xe,%ah # BIOS: Display 364 int $0x10 # character 365 popw %bx # Restore
| 540 pushw %bx # Save 541 movw $0x7,%bx # Page:attribute 542 movb $0xe,%ah # BIOS: Display 543 int $0x10 # character 544 popw %bx # Restore
|
366 retw # To caller
| |
367#else /* SIO */
| 545#else /* SIO */
|
368putchr:
| |
369 movb $0x01,%ah # BIOS: Send 370bioscom: 371 pushw %dx # Save 372 xorw %dx,%dx # Use COM1 373 int $0x14 # Character 374 popw %dx # Restore
| 546 movb $0x01,%ah # BIOS: Send 547bioscom: 548 pushw %dx # Save 549 xorw %dx,%dx # Use COM1 550 int $0x14 # Character 551 popw %dx # Restore
|
375 retw # To caller
| |
376#endif /* SIO */
| 552#endif /* SIO */
|
| 553 retw # To caller
|
377 378/* One-sector disk I/O routine */ 379
| 554 555/* One-sector disk I/O routine */ 556
|
380intx13: movb 0x1(%si),%dh # Load head
| 557/* 558 * %dl: drive, %si partition entry, %es:%bx transfer buffer. 559 * Load the CHS values and possibly the LBA address from the block 560 * at %si, and use the appropriate method to load the sector. 561 * Don't use packet mode for a floppy. 562 */ 563intx13: # Prepare CHS parameters 564 movb 0x1(%si),%dh # Load head
|
381 movw 0x2(%si),%cx # Load cylinder:sector 382 movb $0x1,%al # Sector count 383 pushw %si # Save 384 movw %sp,%di # Save
| 565 movw 0x2(%si),%cx # Load cylinder:sector 566 movb $0x1,%al # Sector count 567 pushw %si # Save 568 movw %sp,%di # Save
|
385 testb $0x80,_FLAGS(%bp) # Use packet interface? 386 jz intx13.1 # No
| 569#ifndef CHECK_DRIVE /* floppy support */ 570 testb %dl, %dl # is this a floppy ? 571 jz 1f # Yes, use CHS mode 572#endif 573 testb $USEPACKET,_FLAGS(%bp) # Use packet interface? 574 jz 1f # No
|
387 pushl $0x0 # Set the 388 pushl 0x8(%si) # LBA address 389 pushw %es # Set the transfer 390 pushw %bx # buffer address 391 push $0x1 # Block count 392 push $0x10 # Packet size 393 movw %sp,%si # Packet pointer 394 decw %ax # Verify off 395 orb $0x40,%ah # Use disk packet
| 575 pushl $0x0 # Set the 576 pushl 0x8(%si) # LBA address 577 pushw %es # Set the transfer 578 pushw %bx # buffer address 579 push $0x1 # Block count 580 push $0x10 # Packet size 581 movw %sp,%si # Packet pointer 582 decw %ax # Verify off 583 orb $0x40,%ah # Use disk packet
|
396intx13.1: int $0x13 # BIOS: Disk I/O
| 584 1: int $0x13 # BIOS: Disk I/O
|
397 movw %di,%sp # Restore 398 popw %si # Restore 399 retw # To caller 400
| 585 movw %di,%sp # Restore 586 popw %si # Restore 587 retw # To caller 588
|
401/* Menu strings */ 402
| 589/* 590 * Various menu strings. 'item' goes after 'prompt' to save space. 591 * Also use shorter versions to make room for the PXE/INT18 code. 592 */ 593#ifdef PXE 594prompt: .ascii "\nBoot:" 595item: .ascii " "; .byte ' '|0x80 596#else 597prompt: .ascii "\nDefault:"
|
403item: .ascii " "; .byte ' '|0x80
| 598item: .ascii " "; .byte ' '|0x80
|
404prompt: .ascii "\nDefault:"; .byte ' '|0x80
| 599#endif
|
405crlf: .ascii "\r"; .byte '\n'|0x80 406 407/* Partition type tables */ 408
| 600crlf: .ascii "\r"; .byte '\n'|0x80 601 602/* Partition type tables */ 603
|
409tables: 410/* 411 * These entries identify invalid or NON BOOT types and partitions. 412 */
| 604non_bootable_ids: 605 /* 606 * These entries identify invalid or NON BOOT types and partitions. 607 * 0: empty, 5: DOS3 ext. partition, 0xf: WIN95 ext partition 608 */
|
413 .byte 0x0, 0x5, 0xf
| 609 .byte 0x0, 0x5, 0xf
|
414/* 415 * These values indicate bootable types we know the names of. 416 */
| 610bootable_ids: 611 /* 612 * These values indicate bootable types we know the names of. 613 * 1: FAT12, 4: FAT16 <32M, 6: FAT16 > 32M, 7: NTFS 614 * 0xb: FAT32, 0xc: FAT32-LBA, 0xe: FAT16-LBA, 615 * 0x83: linux, 0xa5: FreeBSD, 0xa6: netbsd, 0xa9:openbsd 616 */
|
417 .byte 0x1, 0x6, 0x7, 0xb, 0xc, 0xe, 0x83
| 617 .byte 0x1, 0x6, 0x7, 0xb, 0xc, 0xe, 0x83
|
418 .byte 0xa5, 0xa6, 0xa9 419/* 420 * These are offsets that match the known names above and point to the strings 421 * that will be printed. os_misc will be used if the search of the above table 422 * runs over. 423 */ 424 .byte os_dos-. # DOS 425 .byte os_dos-. # DOS 426 .byte os_dos-. # Windows 427 .byte os_dos-. # Windows 428 .byte os_dos-. # Windows 429 .byte os_dos-. # Windows 430 .byte os_linux-. # Linux 431 .byte os_freebsd-. # FreeBSD 432 .byte os_bsd-. # OpenBSD 433 .byte os_bsd-. # NetBSD
| 618 .byte 0xa5, 0xa6, 0xa9, 0x4 619desc_ofs: 620 /* 621 * Offsets that match the known types above, used to point to the 622 * actual partition name. The last entry must point to os_misc, 623 * which is used for non-matching names. 624 */ 625 .byte os_dos-. # 1, DOS 626 .byte os_dos-. # 6, DOS/WIN 627 .byte os_win-. # 7, Windows 628 .byte os_win-. # 11, Windows 629 .byte os_win-. # 12, Windows 630 .byte os_win-. # 14, Windows 631 .byte os_linux-. # 131, Linux 632 .byte os_freebsd-. # 165, FreeBSD 633 .byte os_bsd-. # 166, OpenBSD 634 .byte os_bsd-. # 169, NetBSD 635 .byte os_dos-. # 4, DOS
|
434 .byte os_misc-. # Unknown
| 636 .byte os_misc-. # Unknown
|
435/* 436 * And here are the strings themselves. 0x80 or'd into a byte indicates 437 * the end of the string. (not so great for Russians but...) 438 */ 439os_misc: .ascii "?"; .byte '?'|0x80 440os_dos: .ascii "DO"; .byte 'S'|0x80
| 637 638 /* 639 * And here are the strings themselves. The last byte of 640 * the string has bit 7 set. 641 */ 642os_misc: .byte '?'|0x80 643os_dos: 644#if !defined(TEST) /* DOS string only if room */ 645 .ascii "DO"; .byte 'S'|0x80 646#endif 647os_win: .ascii "WI"; .byte 'N'|0x80
|
441os_linux: .ascii "Linu"; .byte 'x'|0x80 442os_freebsd: .ascii "Free" 443os_bsd: .ascii "BS"; .byte 'D'|0x80 444
| 648os_linux: .ascii "Linu"; .byte 'x'|0x80 649os_freebsd: .ascii "Free" 650os_bsd: .ascii "BS"; .byte 'D'|0x80 651
|
445 .org PRT_OFF-0xe,0x90 446 447 .word B0MAGIC # Magic number 448
| 652 .org B0_OFF,0x90
|
449/*
| 653/*
|
450 * These values are sometimes changed before writing back to the drive
| 654 * The boot0 version 1.0 parameter table. 655 * Do not move it nor change the "Drive " string, boot0cfg 656 * uses its offset and content to identify the boot sector. 657 * The other fields are sometimes changed before writing back to the drive
|
451 * Be especially careful that nxtdrv: must come after drive:, as it 452 * is part of the same string. 453 */ 454drive: .ascii "Drive " 455nxtdrv: .byte 0x0 # Next drive number 456opt: .byte 0x0 # Option
| 658 * Be especially careful that nxtdrv: must come after drive:, as it 659 * is part of the same string. 660 */ 661drive: .ascii "Drive " 662nxtdrv: .byte 0x0 # Next drive number 663opt: .byte 0x0 # Option
|
457setdrv: .byte 0x80 # Drive to force
| 664setdrv_num: .byte 0x80 # Drive to force
|
458flags: .byte FLAGS # Flags 459ticks: .word TICKS # Delay 460
| 665flags: .byte FLAGS # Flags 666ticks: .word TICKS # Delay 667
|
| 668 .org PRT_OFF
|
461/* 462 * Here is the 64 byte partition table that fdisk would fiddle with. 463 */ 464partbl: .fill 0x40,0x1,0x0 # Partition table 465 .word MAGIC # Magic number
| 669/* 670 * Here is the 64 byte partition table that fdisk would fiddle with. 671 */ 672partbl: .fill 0x40,0x1,0x0 # Partition table 673 .word MAGIC # Magic number
|
| 674 .org 0x200 # again, safety check 675endblock:
|
| |