188 lines
4.7 KiB
ArmAsm
188 lines
4.7 KiB
ArmAsm
# Copyright (C) 2015 The University of Notre Dame
|
|
# This software is distributed under the GNU General Public License.
|
|
# See the file LICENSE for details.
|
|
|
|
# This is the raw bootblock code, a 512-byte chunk of assembly
|
|
# found on the first sector of the boot disk. The BIOS is responsible
|
|
# for starting the machine, loading this sector into memory,
|
|
# and then transferring control here. The bootblock must call
|
|
# to the BIOS to load the remaining sectors containing the
|
|
# kernel code, and then jump there.
|
|
|
|
# Constants describing our basic memory layout are in this
|
|
# header file, which is shared between C and assembly modules:
|
|
|
|
#include "memorylayout.h"
|
|
|
|
# When we receive control from the BIOS, the following are set:
|
|
# %dl - the device number this block was loaded from
|
|
# %es:%si - the partition table entry we were loaded from
|
|
|
|
# To set the code segment appropriately, the first thing we
|
|
# do is a long jump to _start2, which sets cs=BOOTBLOCK_SEGMENT
|
|
|
|
.code16
|
|
.text
|
|
.global _start
|
|
_start:
|
|
ljmp $BOOTBLOCK_SEGMENT,$_start2
|
|
|
|
# Now we begin setting up the execution environment
|
|
# for loading the rest of the kernel.
|
|
|
|
_start2:
|
|
sti # disable interrupts
|
|
cld # clear the direction flag
|
|
mov %cs, %ax # set all segments to code
|
|
mov %ax, %ds
|
|
mov %ax, %es
|
|
mov $INTERRUPT_STACK_SEGMENT, %ax # set up the stack
|
|
mov %ax, %ss
|
|
mov $INTERRUPT_STACK_OFFSET, %sp
|
|
|
|
mov %dl, (disk_number) # save the disk number
|
|
mov partition_status,%di # set the partition table as dest
|
|
mov $12, %cx # copy 12 bytes from si to di
|
|
rep movsb
|
|
|
|
mov $(loadmsg),%si # print initial message
|
|
call bios_putstring
|
|
|
|
mov $0,%ah # reset the disk system
|
|
int $0x13
|
|
|
|
mov $0x08, %ah # get the drive geometry
|
|
int $0x13
|
|
and $0x3f, %cl # mask off high tracks
|
|
mov %cl, (disk_sectors)
|
|
mov %ch, (disk_cylinders)
|
|
mov %dh, (disk_heads)
|
|
|
|
mov $KERNEL_SEGMENT,%ax # load happens at es:bx
|
|
mov %ax, %es # which we set to
|
|
mov $KERNEL_OFFSET,%bx # KERNEL_SEGMENT:KERNEL_OFFSET
|
|
|
|
# disk parameters:
|
|
mov (disk_number), %dl # device
|
|
mov $0,%ch # cylinder 0
|
|
mov $0,%dh # head 0
|
|
mov $2,%cl # sector 2
|
|
|
|
loadsector:
|
|
mov $1,%al # load 1 sector
|
|
mov $0x02, %ah # load command
|
|
int $0x13 # execute load
|
|
|
|
mov $'.', %al # display a dot
|
|
call bios_putchar # for each sector loaded
|
|
|
|
mov (sectors_left),%ax # how many sectors left?
|
|
cmp $0xffff, %ax # has it been initialized?
|
|
jne gotsectors # yes - use the value
|
|
mov %es:(KERNEL_SIZE_OFFSET),%eax # no - get size of kernel
|
|
shr $9, %eax # convert into blocks
|
|
inc %eax # add one for good measure
|
|
|
|
gotsectors:
|
|
dec %ax # remove one block
|
|
mov %ax,(sectors_left) # store the value
|
|
cmp $0, %ax # are we done?
|
|
je loaddone # yes - jump to bottom
|
|
|
|
checksegment:
|
|
add $512,%bx # move data pointer by 512 bytes
|
|
cmp $0, %bx # did we reach segment end?
|
|
jnz nextsector # no - find next sector
|
|
mov %es, %ax # yes - retrieve seg register
|
|
add $0x1000, %ax # move to next 64k block
|
|
mov %ax, %es # store segment register
|
|
|
|
nextsector:
|
|
inc %cl # advance by one sector
|
|
mov (disk_sectors),%al # what is the maximum sector?
|
|
cmp %al, %cl # is this the last sector?
|
|
jle loadsector # no - load the next sector
|
|
mov $1,%cl # yes - go to sector zero..
|
|
|
|
inc %dh # advance to next head
|
|
mov (disk_heads), %al # what is the maximum head?
|
|
cmp %al, %dh # is this the last head?
|
|
jle loadsector # no - read the next sector
|
|
mov $0,%dh # yes - go to head zero
|
|
|
|
inc %ch # advance to next cylinder
|
|
mov (disk_cylinders), %al # what is the maximum cylinder?
|
|
cmp %al, %ch # is this the last cylinder?
|
|
jle loadsector # no - read the next sector
|
|
# yes - fall through here
|
|
loaddone:
|
|
mov $0,%ah # reset the disk system
|
|
int $0x13
|
|
|
|
mov $(bootmsg),%si # print boot message
|
|
call bios_putstring
|
|
|
|
mov $KERNEL_SEGMENT, %ax # jump to the kernel code
|
|
mov %ax, %ds
|
|
ljmp $KERNEL_SEGMENT, $KERNEL_OFFSET
|
|
|
|
bios_putstring: # routine to print an entire string
|
|
mov (%si), %al
|
|
cmp $0, %al
|
|
jz bios_putstring_done
|
|
call bios_putchar
|
|
inc %si
|
|
jmp bios_putstring
|
|
bios_putstring_done:
|
|
ret
|
|
|
|
bios_putchar: # routine to print a single char
|
|
push %ax
|
|
push %bx
|
|
mov $14,%ah
|
|
mov $1,%bl
|
|
int $0x10
|
|
pop %bx
|
|
pop %ax
|
|
ret
|
|
|
|
loadmsg:
|
|
.asciz "bootblock: loading kernel...\r\n"
|
|
bootmsg:
|
|
.asciz "\r\nbootblock: booting kernel...\r\n"
|
|
|
|
disk_number:
|
|
.byte 0
|
|
disk_cylinders:
|
|
.byte 0
|
|
disk_heads:
|
|
.byte 0
|
|
disk_sectors:
|
|
.byte 0
|
|
sectors_left:
|
|
.word 0xffff
|
|
|
|
partition_status:
|
|
.byte 0
|
|
partition_start_chs:
|
|
.byte 0
|
|
.byte 0
|
|
.byte 0
|
|
partition_type:
|
|
.byte 0
|
|
partition_stop_chs:
|
|
.byte 0
|
|
.byte 0
|
|
.byte 0
|
|
partition_start_lba:
|
|
.long 0
|
|
partition_length:
|
|
.long 0
|
|
|
|
# A bootblock must have 0xaa55 in its two final bytes.
|
|
# The .org directive forces this data to that point.
|
|
|
|
.org 510
|
|
bootflag:
|
|
.word 0xaa55
|