1 /* 2 * GRUB -- GRand Unified Bootloader 3 * Copyright (C) 1999,2000,2001 Free Software Foundation, Inc. 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 */ 19 20 #define ASM_FILE 21 #include <shared.h> 22 23 #ifndef STAGE1_5 24 #include <stage2_size.h> 25 #endif 26 27 /* 28 * defines for the code go here 29 */ 30 31 /* Absolute addresses 32 This makes the assembler generate the address without support 33 from the linker. (ELF can't relocate 16-bit addresses!) */ 34 #ifdef STAGE1_5 35 # define ABS(x) (x-_start+0x2000) 36 #else 37 # define ABS(x) (x-_start+0x8000) 38 #endif /* STAGE1_5 */ 39 40 /* Print message string */ 41 #define MSG(x) movw $ABS(x), %si; call message 42 43 .file "start.S" 44 45 .text 46 47 /* Tell GAS to generate 16-bit instructions so that this code works 48 in real mode. */ 49 .code16 50 51 .globl start, _start 52 start: 53 _start: 54 /* 55 * _start is loaded at 0x8000 and is jumped to with 56 * CS:IP 0:0x8000 in stage2. 57 */ 58 59 /* 60 * we continue to use the stack for stage1 and assume that 61 * some registers are set to correct values. See stage1.S 62 * for more information. 63 */ 64 65 /* save drive reference first thing! */ 66 pushw %dx 67 68 /* print a notification message on the screen */ 69 pushw %si 70 MSG(notification_string) 71 popw %si 72 73 /* this sets up for the first run through "bootloop" */ 74 movw $ABS(firstlist - BOOTSEC_LISTSIZE), %di 75 76 /* save the sector number of the second sector in %ebp */ 77 movl (%di), %ebp 78 79 /* this is the loop for reading the secondary boot-loader in */ 80 bootloop: 81 82 /* check the number of sectors to read */ 83 cmpw $0, 4(%di) 84 85 /* if zero, go to the start function */ 86 je bootit 87 88 setup_sectors: 89 /* check if we use LBA or CHS */ 90 cmpb $0, -1(%si) 91 92 /* jump to chs_mode if zero */ 93 je chs_mode 94 95 lba_mode: 96 /* load logical sector start */ 97 movl (%di), %ebx 98 99 /* the maximum is limited to 0x7f because of Phoenix EDD */ 100 xorl %eax, %eax 101 movb $0x7f, %al 102 103 /* how many do we really want to read? */ 104 cmpw %ax, 4(%di) /* compare against total number of sectors */ 105 106 /* which is greater? */ 107 jg 1f 108 109 /* if less than, set to total */ 110 movw 4(%di), %ax 111 112 1: 113 /* subtract from total */ 114 subw %ax, 4(%di) 115 116 /* add into logical sector start */ 117 addl %eax, (%di) 118 119 /* set up disk address packet */ 120 121 /* the size and the reserved byte */ 122 movw $0x0010, (%si) 123 124 /* the number of sectors */ 125 movw %ax, 2(%si) 126 127 /* the absolute address (low 32 bits) */ 128 movl %ebx, 8(%si) 129 130 /* the segment of buffer address */ 131 movw $BUFFERSEG, 6(%si) 132 133 /* save %ax from destruction! */ 134 pushw %ax 135 136 /* zero %eax */ 137 xorl %eax, %eax 138 139 /* the offset of buffer address */ 140 movw %ax, 4(%si) 141 142 /* the absolute address (high 32 bits) */ 143 movl %eax, 12(%si) 144 145 146 /* 147 * BIOS call "INT 0x13 Function 0x42" to read sectors from disk into memory 148 * Call with %ah = 0x42 149 * %dl = drive number 150 * %ds:%si = segment:offset of disk address packet 151 * Return: 152 * %al = 0x0 on success; err code on failure 153 */ 154 155 movb $0x42, %ah 156 int $0x13 157 158 jc read_error 159 160 movw $BUFFERSEG, %bx 161 jmp copy_buffer 162 163 chs_mode: 164 /* load logical sector start (bottom half) */ 165 movl (%di), %eax 166 167 /* zero %edx */ 168 xorl %edx, %edx 169 170 /* divide by number of sectors */ 171 divl (%si) 172 173 /* save sector start */ 174 movb %dl, 10(%si) 175 176 xorl %edx, %edx /* zero %edx */ 177 divl 4(%si) /* divide by number of heads */ 178 179 /* save head start */ 180 movb %dl, 11(%si) 181 182 /* save cylinder start */ 183 movw %ax, 12(%si) 184 185 /* do we need too many cylinders? */ 186 cmpw 8(%si), %ax 187 jge geometry_error 188 189 /* determine the maximum sector length of this read */ 190 movw (%si), %ax /* get number of sectors per track/head */ 191 192 /* subtract sector start */ 193 subb 10(%si), %al 194 195 /* how many do we really want to read? */ 196 cmpw %ax, 4(%di) /* compare against total number of sectors */ 197 198 199 /* which is greater? */ 200 jg 2f 201 202 /* if less than, set to total */ 203 movw 4(%di), %ax 204 205 2: 206 /* subtract from total */ 207 subw %ax, 4(%di) 208 209 /* add into logical sector start */ 210 addl %eax, (%di) 211 212 /* 213 * This is the loop for taking care of BIOS geometry translation (ugh!) 214 */ 215 216 /* get high bits of cylinder */ 217 movb 13(%si), %dl 218 219 shlb $6, %dl /* shift left by 6 bits */ 220 movb 10(%si), %cl /* get sector */ 221 222 incb %cl /* normalize sector (sectors go 223 from 1-N, not 0-(N-1) ) */ 224 orb %dl, %cl /* composite together */ 225 movb 12(%si), %ch /* sector+hcyl in cl, cylinder in ch */ 226 227 /* restore %dx */ 228 popw %dx 229 pushw %dx 230 231 /* head number */ 232 movb 11(%si), %dh 233 234 pushw %ax /* save %ax from destruction! */ 235 236 /* 237 * BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory 238 * Call with %ah = 0x2 239 * %al = number of sectors 240 * %ch = cylinder 241 * %cl = sector (bits 6-7 are high bits of "cylinder") 242 * %dh = head 243 * %dl = drive (0x80 for hard disk, 0x0 for floppy disk) 244 * %es:%bx = segment:offset of buffer 245 * Return: 246 * %al = 0x0 on success; err code on failure 247 */ 248 249 movw $BUFFERSEG, %bx 250 movw %bx, %es /* load %es segment with disk buffer */ 251 252 xorw %bx, %bx /* %bx = 0, put it at 0 in the segment */ 253 movb $0x2, %ah /* function 2 */ 254 int $0x13 255 256 jc read_error 257 258 /* save source segment */ 259 movw %es, %bx 260 261 copy_buffer: 262 263 /* load addresses for copy from disk buffer to destination */ 264 movw 6(%di), %es /* load destination segment */ 265 266 /* restore %ax */ 267 popw %ax 268 269 /* determine the next possible destination address (presuming 270 512 byte sectors!) */ 271 shlw $5, %ax /* shift %ax five bits to the left */ 272 addw %ax, 6(%di) /* add the corrected value to the destination 273 address for next time */ 274 275 /* save addressing regs */ 276 pusha 277 pushw %ds 278 279 /* get the copy length */ 280 shlw $4, %ax 281 movw %ax, %cx 282 283 xorw %di, %di /* zero offset of destination addresses */ 284 xorw %si, %si /* zero offset of source addresses */ 285 movw %bx, %ds /* restore the source segment */ 286 287 cld /* sets the copy direction to forward */ 288 289 /* perform copy */ 290 rep /* sets a repeat */ 291 movsb /* this runs the actual copy */ 292 293 /* restore addressing regs and print a dot with correct DS 294 (MSG modifies SI, which is saved, and unused AX and BX) */ 295 popw %ds 296 MSG(notification_step) 297 popa 298 299 /* check if finished with this dataset */ 300 cmpw $0, 4(%di) 301 jne setup_sectors 302 303 /* update position to load from */ 304 subw $BOOTSEC_LISTSIZE, %di 305 306 /* jump to bootloop */ 307 jmp bootloop 308 309 /* END OF MAIN LOOP */ 310 311 bootit: 312 /* print a newline */ 313 MSG(notification_done) 314 popw %dx /* this makes sure %dl is our "boot" drive */ 315 #ifdef STAGE1_5 316 ljmp $0, $0x2200 317 #else /* ! STAGE1_5 */ 318 ljmp $0, $0x8200 319 #endif /* ! STAGE1_5 */ 320 321 322 /* 323 * BIOS Geometry translation error (past the end of the disk geometry!). 324 */ 325 geometry_error: 326 MSG(geometry_error_string) 327 jmp general_error 328 329 /* 330 * Read error on the disk. 331 */ 332 read_error: 333 MSG(read_error_string) 334 335 general_error: 336 MSG(general_error_string) 337 338 /* go here when you need to stop the machine hard after an error condition */ 339 stop: jmp stop 340 341 #ifdef STAGE1_5 342 notification_string: .string "Loading stage1.5" 343 #else 344 notification_string: .string "Loading stage2" 345 #endif 346 347 notification_step: .string "." 348 notification_done: .string "\r\n" 349 350 geometry_error_string: .string "Geom" 351 read_error_string: .string "Read" 352 general_error_string: .string " Error" 353 354 /* 355 * message: write the string pointed to by %si 356 * 357 * WARNING: trashes %si, %ax, and %bx 358 */ 359 360 /* 361 * Use BIOS "int 10H Function 0Eh" to write character in teletype mode 362 * %ah = 0xe %al = character 363 * %bh = page %bl = foreground color (graphics modes) 364 */ 365 1: 366 movw $0x0001, %bx 367 movb $0xe, %ah 368 int $0x10 /* display a byte */ 369 370 incw %si 371 message: 372 movb (%si), %al 373 cmpb $0, %al 374 jne 1b /* if not end of string, jmp to display */ 375 ret 376 lastlist: 377 378 /* 379 * This area is an empty space between the main body of code below which 380 * grows up (fixed after compilation, but between releases it may change 381 * in size easily), and the lists of sectors to read, which grows down 382 * from a fixed top location. 383 */ 384 385 .word 0 386 .word 0 387 388 . = _start + 0x200 - BOOTSEC_LISTSIZE 389 390 /* fill the first data listing with the default */ 391 blocklist_default_start: 392 .long 2 /* this is the sector start parameter, in logical 393 sectors from the start of the disk, sector 0 */ 394 blocklist_default_len: 395 /* this is the number of sectors to read */ 396 #ifdef STAGE1_5 397 .word 0 /* the command "install" will fill this up */ 398 #else 399 .word (STAGE2_SIZE + 511) >> 9 400 #endif 401 blocklist_default_seg: 402 #ifdef STAGE1_5 403 .word 0x220 404 #else 405 .word 0x820 /* this is the segment of the starting address 406 to load the data into */ 407 #endif 408 409 firstlist: /* this label has to be after the list data!!! */ 410