linux-old/boot/boot.s
<<
>>
Prefs
   1|
   2|       boot.s
   3|
   4| boot.s is loaded at 0x7c00 by the bios-startup routines, and moves itself
   5| out of the way to address 0x90000, and jumps there.
   6|
   7| It then loads the system at 0x10000, using BIOS interrupts. Thereafter
   8| it disables all interrupts, moves the system down to 0x0000, changes
   9| to protected mode, and calls the start of system. System then must
  10| RE-initialize the protected mode in it's own tables, and enable
  11| interrupts as needed.
  12|
  13| NOTE! currently system is at most 8*65536 bytes long. This should be no
  14| problem, even in the future. I want to keep it simple. This 512 kB
  15| kernel size should be enough - in fact more would mean we'd have to move
  16| not just these start-up routines, but also do something about the cache-
  17| memory (block IO devices). The area left over in the lower 640 kB is meant
  18| for these. No other memory is assumed to be "physical", ie all memory
  19| over 1Mb is demand-paging. All addresses under 1Mb are guaranteed to match
  20| their physical addresses.
  21|
  22| NOTE1 abouve is no longer valid in it's entirety. cache-memory is allocated
  23| above the 1Mb mark as well as below. Otherwise it is mainly correct.
  24|
  25| NOTE 2! The boot disk type must be set at compile-time, by setting
  26| the following equ. Having the boot-up procedure hunt for the right
  27| disk type is severe brain-damage.
  28| The loader has been made as simple as possible (had to, to get it
  29| in 512 bytes with the code to move to protected mode), and continuos
  30| read errors will result in a unbreakable loop. Reboot by hand. It
  31| loads pretty fast by getting whole sectors at a time whenever possible.
  32
  33| 1.44Mb disks:
  34sectors = 18
  35| 1.2Mb disks:
  36| sectors = 15
  37| 720kB disks:
  38| sectors = 9
  39
  40.globl begtext, begdata, begbss, endtext, enddata, endbss
  41.text
  42begtext:
  43.data
  44begdata:
  45.bss
  46begbss:
  47.text
  48
  49BOOTSEG = 0x07c0
  50INITSEG = 0x9000
  51SYSSEG  = 0x1000                        | system loaded at 0x10000 (65536).
  52ENDSEG  = SYSSEG + SYSSIZE
  53
  54entry start
  55start:
  56        mov     ax,#BOOTSEG
  57        mov     ds,ax
  58        mov     ax,#INITSEG
  59        mov     es,ax
  60        mov     cx,#256
  61        sub     si,si
  62        sub     di,di
  63        rep
  64        movw
  65        jmpi    go,INITSEG
  66go:     mov     ax,cs
  67        mov     ds,ax
  68        mov     es,ax
  69        mov     ss,ax
  70        mov     sp,#0x400               | arbitrary value >>512
  71
  72        mov     ah,#0x03        | read cursor pos
  73        xor     bh,bh
  74        int     0x10
  75        
  76        mov     cx,#24
  77        mov     bx,#0x0007      | page 0, attribute 7 (normal)
  78        mov     bp,#msg1
  79        mov     ax,#0x1301      | write string, move cursor
  80        int     0x10
  81
  82| ok, we've written the message, now
  83| we want to load the system (at 0x10000)
  84
  85        mov     ax,#SYSSEG
  86        mov     es,ax           | segment of 0x010000
  87        call    read_it
  88        call    kill_motor
  89
  90| if the read went well we get current cursor position ans save it for
  91| posterity.
  92
  93        mov     ah,#0x03        | read cursor pos
  94        xor     bh,bh
  95        int     0x10            | save it in known place, con_init fetches
  96        mov     [510],dx        | it from 0x90510.
  97                
  98| now we want to move to protected mode ...
  99
 100        cli                     | no interrupts allowed !
 101
 102| first we move the system to it's rightful place
 103
 104        mov     ax,#0x0000
 105        cld                     | 'direction'=0, movs moves forward
 106do_move:
 107        mov     es,ax           | destination segment
 108        add     ax,#0x1000
 109        cmp     ax,#0x9000
 110        jz      end_move
 111        mov     ds,ax           | source segment
 112        sub     di,di
 113        sub     si,si
 114        mov     cx,#0x8000
 115        rep
 116        movsw
 117        j       do_move
 118
 119| then we load the segment descriptors
 120
 121end_move:
 122
 123        mov     ax,cs           | right, forgot this at first. didn't work :-)
 124        mov     ds,ax
 125        lidt    idt_48          | load idt with 0,0
 126        lgdt    gdt_48          | load gdt with whatever appropriate
 127
 128| that was painless, now we enable A20
 129
 130        call    empty_8042
 131        mov     al,#0xD1                | command write
 132        out     #0x64,al
 133        call    empty_8042
 134        mov     al,#0xDF                | A20 on
 135        out     #0x60,al
 136        call    empty_8042
 137
 138| well, that went ok, I hope. Now we have to reprogram the interrupts :-(
 139| we put them right after the intel-reserved hardware interrupts, at
 140| int 0x20-0x2F. There they won't mess up anything. Sadly IBM really
 141| messed this up with the original PC, and they haven't been able to
 142| rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,
 143| which is used for the internal hardware interrupts as well. We just
 144| have to reprogram the 8259's, and it isn't fun.
 145
 146        mov     al,#0x11                | initialization sequence
 147        out     #0x20,al                | send it to 8259A-1
 148        .word   0x00eb,0x00eb           | jmp $+2, jmp $+2
 149        out     #0xA0,al                | and to 8259A-2
 150        .word   0x00eb,0x00eb
 151        mov     al,#0x20                | start of hardware int's (0x20)
 152        out     #0x21,al
 153        .word   0x00eb,0x00eb
 154        mov     al,#0x28                | start of hardware int's 2 (0x28)
 155        out     #0xA1,al
 156        .word   0x00eb,0x00eb
 157        mov     al,#0x04                | 8259-1 is master
 158        out     #0x21,al
 159        .word   0x00eb,0x00eb
 160        mov     al,#0x02                | 8259-2 is slave
 161        out     #0xA1,al
 162        .word   0x00eb,0x00eb
 163        mov     al,#0x01                | 8086 mode for both
 164        out     #0x21,al
 165        .word   0x00eb,0x00eb
 166        out     #0xA1,al
 167        .word   0x00eb,0x00eb
 168        mov     al,#0xFF                | mask off all interrupts for now
 169        out     #0x21,al
 170        .word   0x00eb,0x00eb
 171        out     #0xA1,al
 172
 173| well, that certainly wasn't fun :-(. Hopefully it works, and we don't
 174| need no steenking BIOS anyway (except for the initial loading :-).
 175| The BIOS-routine wants lots of unnecessary data, and it's less
 176| "interesting" anyway. This is how REAL programmers do it.
 177|
 178| Well, now's the time to actually move into protected mode. To make
 179| things as simple as possible, we do no register set-up or anything,
 180| we let the gnu-compiled 32-bit programs do that. We just jump to
 181| absolute address 0x00000, in 32-bit protected mode.
 182
 183        mov     ax,#0x0001      | protected mode (PE) bit
 184        lmsw    ax              | This is it!
 185        jmpi    0,8             | jmp offset 0 of segment 8 (cs)
 186
 187| This routine checks that the keyboard command queue is empty
 188| No timeout is used - if this hangs there is something wrong with
 189| the machine, and we probably couldn't proceed anyway.
 190empty_8042:
 191        .word   0x00eb,0x00eb
 192        in      al,#0x64        | 8042 status port
 193        test    al,#2           | is input buffer full?
 194        jnz     empty_8042      | yes - loop
 195        ret
 196
 197| This routine loads the system at address 0x10000, making sure
 198| no 64kB boundaries are crossed. We try to load it as fast as
 199| possible, loading whole tracks whenever we can.
 200|
 201| in:   es - starting address segment (normally 0x1000)
 202|
 203| This routine has to be recompiled to fit another drive type,
 204| just change the "sectors" variable at the start of the file
 205| (originally 18, for a 1.44Mb drive)
 206|
 207sread:  .word 1                 | sectors read of current track
 208head:   .word 0                 | current head
 209track:  .word 0                 | current track
 210read_it:
 211        mov ax,es
 212        test ax,#0x0fff
 213die:    jne die                 | es must be at 64kB boundary
 214        xor bx,bx               | bx is starting address within segment
 215rp_read:
 216        mov ax,es
 217        cmp ax,#ENDSEG          | have we loaded all yet?
 218        jb ok1_read
 219        ret
 220ok1_read:
 221        mov ax,#sectors
 222        sub ax,sread
 223        mov cx,ax
 224        shl cx,#9
 225        add cx,bx
 226        jnc ok2_read
 227        je ok2_read
 228        xor ax,ax
 229        sub ax,bx
 230        shr ax,#9
 231ok2_read:
 232        call read_track
 233        mov cx,ax
 234        add ax,sread
 235        cmp ax,#sectors
 236        jne ok3_read
 237        mov ax,#1
 238        sub ax,head
 239        jne ok4_read
 240        inc track
 241ok4_read:
 242        mov head,ax
 243        xor ax,ax
 244ok3_read:
 245        mov sread,ax
 246        shl cx,#9
 247        add bx,cx
 248        jnc rp_read
 249        mov ax,es
 250        add ax,#0x1000
 251        mov es,ax
 252        xor bx,bx
 253        jmp rp_read
 254
 255read_track:
 256        push ax
 257        push bx
 258        push cx
 259        push dx
 260        mov dx,track
 261        mov cx,sread
 262        inc cx
 263        mov ch,dl
 264        mov dx,head
 265        mov dh,dl
 266        mov dl,#0
 267        and dx,#0x0100
 268        mov ah,#2
 269        int 0x13
 270        jc bad_rt
 271        pop dx
 272        pop cx
 273        pop bx
 274        pop ax
 275        ret
 276bad_rt: mov ax,#0
 277        mov dx,#0
 278        int 0x13
 279        pop dx
 280        pop cx
 281        pop bx
 282        pop ax
 283        jmp read_track
 284
 285/*
 286 * This procedure turns off the floppy drive motor, so
 287 * that we enter the kernel in a known state, and
 288 * don't have to worry about it later.
 289 */
 290kill_motor:
 291        push dx
 292        mov dx,#0x3f2
 293        mov al,#0
 294        outb
 295        pop dx
 296        ret
 297
 298gdt:
 299        .word   0,0,0,0         | dummy
 300
 301        .word   0x07FF          | 8Mb - limit=2047 (2048*4096=8Mb)
 302        .word   0x0000          | base address=0
 303        .word   0x9A00          | code read/exec
 304        .word   0x00C0          | granularity=4096, 386
 305
 306        .word   0x07FF          | 8Mb - limit=2047 (2048*4096=8Mb)
 307        .word   0x0000          | base address=0
 308        .word   0x9200          | data read/write
 309        .word   0x00C0          | granularity=4096, 386
 310
 311idt_48:
 312        .word   0                       | idt limit=0
 313        .word   0,0                     | idt base=0L
 314
 315gdt_48:
 316        .word   0x800           | gdt limit=2048, 256 GDT entries
 317        .word   gdt,0x9         | gdt base = 0X9xxxx
 318        
 319msg1:
 320        .byte 13,10
 321        .ascii "Loading system ..."
 322        .byte 13,10,13,10
 323
 324.text
 325endtext:
 326.data
 327enddata:
 328.bss
 329endbss:
 330
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.