Linux/boot/bootsect.S
1 !2 ! SYS_SIZE is the number of clicks (16 bytes) to be loaded.3 ! 0x7F00 is 0x7F000 bytes = 508kB, more than enough for current4 ! versions of linux which compress the kernel5 !6 #include <linux/config.h>7 SYSSIZE = DEF_SYSSIZE8 !9 ! bootsect.s Copyright (C) 1991, 1992 Linus Torvalds10 ! modified by Drew Eckhardt11 ! modified by Bruce Evans (bde)12 !13 ! bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves14 ! itself out of the way to address 0x90000, and jumps there.15 !16 ! bde - should not jump blindly, there may be systems with only 512K low17 ! memory. Use int 0x12 to get the top of memory, etc.18 !19 ! It then loads 'setup' directly after itself (0x90200), and the system20 ! at 0x10000, using BIOS interrupts.21 !22 ! NOTE! currently system is at most (8*65536-4096) bytes long. This should23 ! be no problem, even in the future. I want to keep it simple. This 508 kB24 ! kernel size should be enough, especially as this doesn't contain the25 ! buffer cache as in minix (and especially now that the kernel is26 ! compressed :-)27 !28 ! The loader has been made as simple as possible, and continuos29 ! read errors will result in a unbreakable loop. Reboot by hand. It30 ! loads pretty fast by getting whole tracks at a time whenever possible.3132 .text3334 SETUPSECS = 4 ! nr of setup-sectors35 BOOTSEG = 0x07C0 ! original address of boot-sector36 INITSEG = DEF_INITSEG ! we move boot here - out of the way37 SETUPSEG = DEF_SETUPSEG ! setup starts here38 SYSSEG = DEF_SYSSEG ! system loaded at 0x10000 (65536).3940 ! ROOT_DEV & SWAP_DEV are now written by "build".41 ROOT_DEV = 042 SWAP_DEV = 043 #ifndef SVGA_MODE44 #define SVGA_MODE ASK_VGA45 #endif46 #ifndef RAMDISK47 #define RAMDISK 048 #endif49 #ifndef CONFIG_ROOT_RDONLY50 #define CONFIG_ROOT_RDONLY 051 #endif5253 ! ld86 requires an entry symbol. This may as well be the usual one.54 .globl _main55 _main:56 #if 0 /* hook for debugger, harmless unless BIOS is fussy (old HP) */57 int 358 #endif59 mov ax,#BOOTSEG60 mov ds,ax61 mov ax,#INITSEG62 mov es,ax63 mov cx,#25664 sub si,si65 sub di,di66 cld67 rep68 movsw69 jmpi go,INITSEG7071 go: mov ax,cs72 mov dx,#0x4000-12 ! 0x4000 is arbitrary value >= length of73 ! bootsect + length of setup + room for stack74 ! 12 is disk parm size7576 ! bde - changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde). We77 ! wouldn't have to worry about this if we checked the top of memory. Also78 ! my BIOS can be configured to put the wini drive tables in high memory79 ! instead of in the vector table. The old stack might have clobbered the80 ! drive table.8182 mov ds,ax83 mov es,ax84 mov ss,ax ! put stack at INITSEG:0x4000-12.85 mov sp,dx86 /*87 * Many BIOS's default disk parameter tables will not88 * recognize multi-sector reads beyond the maximum sector number89 * specified in the default diskette parameter tables - this may90 * mean 7 sectors in some cases.91 *92 * Since single sector reads are slow and out of the question,93 * we must take care of this by creating new parameter tables94 * (for the first disk) in RAM. We will set the maximum sector95 * count to 18 - the most we will encounter on an HD 1.44.96 *97 * High doesn't hurt. Low does.98 *99 * Segments are as follows: ds=es=ss=cs - INITSEG,100 * fs = 0, gs = parameter table segment101 */102103 push #0104 pop fs105 mov bx,#0x78 ! fs:bx is parameter table address106 seg fs107 lgs si,(bx) ! gs:si is source108109 mov di,dx ! es:di is destination110 mov cx,#6 ! copy 12 bytes111 cld112113 rep114 seg gs115 movsw116117 mov di,dx118 movb 4(di),*18 ! patch sector count119120 seg fs121 mov (bx),di122 seg fs123 mov 2(bx),es124125 mov ax,cs126 mov fs,ax127 mov gs,ax128129 xor ah,ah ! reset FDC130 xor dl,dl131 int 0x13132133 ! load the setup-sectors directly after the bootblock.134 ! Note that 'es' is already set up.135136 load_setup:137 xor dx, dx ! drive 0, head 0138 mov cx,#0x0002 ! sector 2, track 0139 mov bx,#0x0200 ! address = 512, in INITSEG140 mov ax,#0x0200+SETUPSECS ! service 2, nr of sectors141 ! (assume all on head 0, track 0)142 int 0x13 ! read it143 jnc ok_load_setup ! ok - continue144145 push ax ! dump error code146 call print_nl147 mov bp, sp148 call print_hex149 pop ax150151 xor dl, dl ! reset FDC152 xor ah, ah153 int 0x13154 jmp load_setup155156 ok_load_setup:157158 ! Get disk drive parameters, specifically nr of sectors/track159160 #if 0161162 ! bde - the Phoenix BIOS manual says function 0x08 only works for fixed163 ! disks. It doesn't work for one of my BIOS's (1987 Award). It was164 ! fatal not to check the error code.165166 xor dl,dl167 mov ah,#0x08 ! AH=8 is get drive parameters168 int 0x13169 xor ch,ch170 #else171172 ! It seems that there is no BIOS call to get the number of sectors. Guess173 ! 18 sectors if sector 18 can be read, 15 if sector 15 can be read.174 ! Otherwise guess 9.175176 xor dx, dx ! drive 0, head 0177 mov cx,#0x0012 ! sector 18, track 0178 mov bx,#0x0200+SETUPSECS*0x200 ! address after setup (es = cs)179 mov ax,#0x0201 ! service 2, 1 sector180 int 0x13181 jnc got_sectors182 mov cl,#0x0f ! sector 15183 mov ax,#0x0201 ! service 2, 1 sector184 int 0x13185 jnc got_sectors186 mov cl,#0x09187188 #endif189190 got_sectors:191 seg cs192 mov sectors,cx193 mov ax,#INITSEG194 mov es,ax195196 ! Print some inane message197198 mov ah,#0x03 ! read cursor pos199 xor bh,bh200 int 0x10201202 mov cx,#9203 mov bx,#0x0007 ! page 0, attribute 7 (normal)204 mov bp,#msg1205 mov ax,#0x1301 ! write string, move cursor206 int 0x10207208 ! ok, we've written the message, now209 ! we want to load the system (at 0x10000)210211 mov ax,#SYSSEG212 mov es,ax ! segment of 0x010000213 call read_it214 call kill_motor215 call print_nl216217 ! After that we check which root-device to use. If the device is218 ! defined (!= 0), nothing is done and the given device is used.219 ! Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending220 ! on the number of sectors that the BIOS reports currently.221222 seg cs223 mov ax,root_dev224 or ax,ax225 jne root_defined226 seg cs227 mov bx,sectors228 mov ax,#0x0208 ! /dev/ps0 - 1.2Mb229 cmp bx,#15230 je root_defined231 mov ax,#0x021c ! /dev/PS0 - 1.44Mb232 cmp bx,#18233 je root_defined234 mov ax,#0x0200 ! /dev/fd0 - autodetect235 root_defined:236 seg cs237 mov root_dev,ax238239 ! after that (everyting loaded), we jump to240 ! the setup-routine loaded directly after241 ! the bootblock:242243 jmpi 0,SETUPSEG244245 ! This routine loads the system at address 0x10000, making sure246 ! no 64kB boundaries are crossed. We try to load it as fast as247 ! possible, loading whole tracks whenever we can.248 !249 ! in: es - starting address segment (normally 0x1000)250 !251 sread: .word 1+SETUPSECS ! sectors read of current track252 head: .word 0 ! current head253 track: .word 0 ! current track254255 read_it:256 mov ax,es257 test ax,#0x0fff258 die: jne die ! es must be at 64kB boundary259 xor bx,bx ! bx is starting address within segment260 rp_read:261 mov ax,es262 sub ax,#SYSSEG263 cmp ax,syssize ! have we loaded all yet?264 jbe ok1_read265 ret266 ok1_read:267 seg cs268 mov ax,sectors269 sub ax,sread270 mov cx,ax271 shl cx,#9272 add cx,bx273 jnc ok2_read274 je ok2_read275 xor ax,ax276 sub ax,bx277 shr ax,#9278 ok2_read:279 call read_track280 mov cx,ax281 add ax,sread282 seg cs283 cmp ax,sectors284 jne ok3_read285 mov ax,#1286 sub ax,head287 jne ok4_read288 inc track289 ok4_read:290 mov head,ax291 xor ax,ax292 ok3_read:293 mov sread,ax294 shl cx,#9295 add bx,cx296 jnc rp_read297 mov ax,es298 add ah,#0x10299 mov es,ax300 xor bx,bx301 jmp rp_read302303 read_track:304 pusha305 pusha306 mov ax, #0xe2e ! loading... message 2e = .307 mov bx, #7308 int 0x10309 popa310311 mov dx,track312 mov cx,sread313 inc cx314 mov ch,dl315 mov dx,head316 mov dh,dl317 and dx,#0x0100318 mov ah,#2319320 push dx ! save for error dump321 push cx322 push bx323 push ax324325 int 0x13326 jc bad_rt327 add sp, #8328 popa329 ret330331 bad_rt: push ax ! save error code332 call print_all ! ah = error, al = read333334335 xor ah,ah336 xor dl,dl337 int 0x13338339340 add sp, #10341 popa342 jmp read_track343344 /*345 * print_all is for debugging purposes.346 * It will print out all of the registers. The assumption is that this is347 * called from a routine, with a stack frame like348 * dx349 * cx350 * bx351 * ax352 * error353 * ret <- sp354 *355 */356357 print_all:358 mov cx, #5 ! error code + 4 registers359 mov bp, sp360361 print_loop:362 push cx ! save count left363 call print_nl ! nl for readability364365 cmp cl, 5366 jae no_reg ! see if register name is needed367368 mov ax, #0xe05 + 'A - 1369 sub al, cl370 int 0x10371372 mov al, #'X373 int 0x10374375 mov al, #':376 int 0x10377378 no_reg:379 add bp, #2 ! next register380 call print_hex ! print it381 pop cx382 loop print_loop383 ret384385 print_nl:386 mov ax, #0xe0d ! CR387 int 0x10388 mov al, #0xa ! LF389 int 0x10390 ret391392 /*393 * print_hex is for debugging purposes, and prints the word394 * pointed to by ss:bp in hexadecmial.395 */396397 print_hex:398 mov cx, #4 ! 4 hex digits399 mov dx, (bp) ! load word into dx400 print_digit:401 rol dx, #4 ! rotate so that lowest 4 bits are used402 mov ah, #0xe403 mov al, dl ! mask off so we have only next nibble404 and al, #0xf405 add al, #'0 ! convert to 0-based digit406 cmp al, #'9 ! check for overflow407 jbe good_digit408 add al, #'A - '0 - 10409410 good_digit:411 int 0x10412 loop print_digit413 ret414415416 /*417 * This procedure turns off the floppy drive motor, so418 * that we enter the kernel in a known state, and419 * don't have to worry about it later.420 */421 kill_motor:422 push dx423 mov dx,#0x3f2424 xor al, al425 outb426 pop dx427 ret428429 sectors:430 .word 0431432 msg1:433 .byte 13,10434 .ascii "Loading"435436 .org 498437 root_flags:438 .word CONFIG_ROOT_RDONLY439 syssize:440 .word SYSSIZE441 swap_dev:442 .word SWAP_DEV443 ram_size:444 .word RAMDISK445 vid_mode:446 .word SVGA_MODE447 root_dev:448 .word ROOT_DEV449 boot_flag:450 .word 0xAA55