/*-
* Copyright (c) 2005, Kohsuke Ohtani
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/**
* bootsect.s - Boot sector for FAT
*
* The boot sector is 512 byte code to load the OS image. It is loaded
* to address 0:7c00 by POST BIOS.
* The boot sector searches the target file within the root directory of
* FAT file system, and loads it to predefined memory address. Then, it
* jumps to the first byte of the loaded image.
*
* All disk access are done by using BIOS Int13h interface. The BIOS
* parameter block (BPB) has the disk/FAT information, and it exists
* in the first portion of the FAT boot sector. It must be filled by the
* FAT format utility, or the prex kernel install utility (mkboot.com).
* This program assumes that correct BPB is stored in the boot sector.
*
* Limitation:
* - Support only FAT12/16. FAT32 is not supported.
*
* Memory usage:
* > 5000 - 6FFF ... Disk work area
* > 7000 - 7BFF ... Stack
* > 7C00 - 7DFF ... This boot sector
* >10000 - ... FAT cache
* >20000 - ... Data cache
* >30000 - ... Image load address
*/
.code16
.text
.align 1
# Memory locations
#define BOOT_STACK 0x7c00
#define LOAD_ADDR 0x30000
#define ENTRY_SEG 0x3000
#define ENTRY_OFF 0x0000
#define WORK_AREA 0x5000
#define FAT_SEG 0x1000
#define DATA_SEG 0x2000
#define LOAD_MAX 0xA0000
# FAT Directory entry
#define F_NAME 0
#define F_ATTR 11
#define F_RESERVED 12
#define F_TIME 22
#define F_DATA 24
#define F_CLUSTER 26
#define F_SIZE 28
#define DIR_SIZE 32
#define DIRENT_PER_SECTOR 16
# BIOS parameter block (BPB) location (%bp points to 0x7c00)
#define OEM_ID 0x03(%bp)
#define BYTE_PER_SECTOR 0x0b(%bp)
#define SECT_PER_CLUSTER 0x0d(%bp)
#define RESERVED_SECTORS 0x0e(%bp)
#define NUM_OF_FATS 0x10(%bp)
#define ROOT_ENTRIES 0x11(%bp)
#define TOTAL_SECTORS 0x13(%bp)
#define MEDIA_DESCRIPTOR 0x15(%bp)
#define SECTORS_PER_FAT 0x16(%bp)
#define SECTORS_PER_TRACK 0x18(%bp)
#define HEADS 0x1a(%bp)
#define HIDDEN_SECTORS 0x1c(%bp)
#define BIG_TOTAL_SECTORS 0x20(%bp)
#define PHYSICAL_DRIVE 0x24(%bp)
#define EXT_BOOT_SIGNATURE 0x26(%bp)
#define SERIAL_NO 0x27(%bp)
#define VOLUME_ID 0x2b(%bp)
#define FILE_SYS_ID 0x36(%bp)
#define FILE_SYS_ID_NUM 0x3a(%bp)
# Local data area (Note: These data will overlap the existing code)
#define FAT_START 0x40(%bp)
#define DATA_START 0x44(%bp)
.global _boot
#
# Boot the system
#
_boot:
jmp start # Skip BPB
nop # Nop is for DOS compatibility
#
# BPB
#
.ascii "PREX1.00"
.fill 0x33, 1, 0 # Drive parameter must be
# filled by intaller
#
# Setup stack and segment registers
#
start:
cli
cld # Clear direction flag
xorl %eax, %eax # Set EAX to zero
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
movw $(BOOT_STACK), %sp
movw %sp, %bp # EBP = Bios Parameter Block
sti
#
# Display boot message
#
movw $load_msg, %si
movw $21, %cx
call puts
#
# Store disk information
#
movl HIDDEN_SECTORS, %ebx # Get hidden sector
movw RESERVED_SECTORS, %ax # Add reserved sector
addl %eax, %ebx # High 16 bit of EAX is 0
movl %ebx, FAT_START # FAT start = hidden + reserved
movzbw NUM_OF_FATS, %ax # Normally 2
mulw SECTORS_PER_FAT # AX = Num of sector of FATs
addl %ebx, %eax # EAX = Start of root directory
movw ROOT_ENTRIES, %bx
shrw $4, %bx # / 16 = DIRENT_PER_SECTOR
movw %bx, %cx # CX = Num of sectors for root directory
addl %eax, %ebx # DATA start = FAT start + root
movl %ebx, DATA_START # Start sector of data area
#
# Find the OS image in the root directory
#
# EAX = Start sector of root
next_sector:
pushw %cx
movl $(WORK_AREA), %ebx
pushw %bx
call read_sector # Read 1 sector in root
popw %di # DI = dir_entry
movw $(DIRENT_PER_SECTOR), %cx # CX = directory count
next_entry:
cmpb $0, (%di) # End of dir entry ?
je error # Not found
testb $0x18, F_ATTR(%di) # Subdir or Volume ?
jnz not_file # Skip it
pusha
movw $11, %cx # File name + ext = 11 byte
movw $image_name, %si
repe # Compare file name
cmpsb
popa
je found_file
not_file:
addw $(DIR_SIZE), %di # Check next directory entry
loop next_entry
popw %cx
loop next_sector
# Fall through
#
# Error case
#
error:
movw $err_msg, %si
movw $5, %cx
call puts
hang:
hlt
jmp hang # Stop here
#
# Load image
#
found_file:
movzwl F_CLUSTER(%di), %eax # EAX = 1st cluster of loader
movl $(LOAD_ADDR), %ebx # EBX = 32bit load address
load_next:
call read_cluster # Read cluster of loader
call next_cluster # Get next cluster# in EAX
jb load_next # EOF ?
#
# Turn fdd motor off
#
movw $0x3f2, %dx
xorb %al, %al
outb %al, %dx
#
# Jump to loaded image
#
ljmp $0x3000, $0x0
#
# Puts - Print string
#
# Entry:
# SI - Pointer to message string
# CX - Number of character
#
puts:
lodsb
movb $0x0e, %ah
movw $0x0007, %bx
int $0x10
loop puts
ret
#
# next_cluster - Return next cluster
#
# Entry:
# EAX - Current cluter#
#
# Exit:
# AF - End of cluster
# EAX - Next cluter#
#
# Modified:
# Flags,CX,EDX,SI,DI
#
next_cluster:
pushl %ebx
movw %ax, %di # Save cluster# in DI
movw $0xfff8, %si # Set default EOF to FAT16
movl %eax, %ecx
shll $1, %eax # * 2
cmpb $0x36, FILE_SYS_ID_NUM # ID is 'FAT16' ?
je fat_16
addl %ecx, %eax # * 3
shrl $1, %eax # / 2
movw $0xff8, %si # EOF for FAT12
fat_16:
# EAX - Offset of FAT entry
xorw %dx, %dx
divw BYTE_PER_SECTOR
addl FAT_START, %eax # EAX = Sector# for FAT
# DX = Offset in sector
movl $(WORK_AREA), %ebx
pushw %bx
call read_sector # Read 2 sector for border
call read_sector # data
popw %bx
addw %dx, %bx
movw (%bx), %ax
cmpw $0xfff8, %si # FAT16 ?
je chk_end
shrw $1, %di
jc odd_pos
andb $0x0f, %ah
jmp chk_end
odd_pos:
shrw $4, %ax
chk_end:
cmpw %si, %ax
popl %ebx
ret
#
# read_cluster - Read one cluster
#
# Entry:
# EBX - 32-bit pointer to buffer
# EAX - Cluster number
#
# Exit:
# EBX - Point to next buffer
#
# Modified:
# flags,ECX,ECX,EDX
#
read_cluster:
pushl %eax
decw %ax # Translate clust# to sec#
decw %ax
xorl %ecx, %ecx
movb SECT_PER_CLUSTER, %cl
mull %ecx
addl DATA_START, %eax # EAX = Read sec#
# CX = Read sector size
read_loop:
call read_sector
cmpl $(LOAD_MAX), %ebx
jae error
loop read_loop
popl %eax
ret
#
# read_sector - Read one sector
#
# Entry:
# EBX - 32-bit pointer to buffer
# EAX - Logical sector# to read
#
# Exit:
# EBX - Pointer to next buffer
# EAX - Next sector
#
# Modified:
# Flags
#
read_sector:
pushal
pushw %ds
pushw %es
movl %eax, %esi # ESI = buffer
movzwl SECTORS_PER_TRACK, %ecx # Get sec/track
xorl %edx, %edx
divl %ecx # EAX = track#
# DX = sec#
movw $(DATA_SEG), %cx # Check in cache
leaw last_data, %di
cmpl DATA_START, %esi
jae data_reqest
movw $(FAT_SEG), %cx
leaw last_fat, %di
data_reqest:
# CX = Cached segment
pushal # [DI] = Cached track
movw %cx, %es
xorw %bx, %bx # ES:BX = Cache address
cmpl (%di), %eax # Last track ?
je hit_cache
movl %eax, (%di) # Save current track#
call read_track
hit_cache:
popal
pushw %es
popw %ds
shlw $9, %dx # sec# * 512
movw %dx, %si # DS:SI = Offset in cache
movw %bx, %di # [EBX] -> ES:[DI]
andw $0xf, %di
shrl $4, %ebx
movw %bx, %es
mov $512, %cx # Copy 1 sector
rep
movsb
popw %es
popw %ds
popal
addl $512, %ebx # Next buffer
incl %eax # Next sector
ret
#
# read_track - Read one track
#
# Entry:
# ES:[BX] - Pointer to buffer
# EAX - Track number to read
#
# Exit:
# None
#
# Modified:
# Flags,EAX,ECX,EDX
#
read_track:
movzwl HEADS, %ecx # Get num of head
xorl %edx, %edx
divl %ecx # AX = cyl#
# DL = head#
movb %al, %ch # CH = cyl# (low 8 bits)
andb $3, %ah
shlb $6, %ah
orb $1, %ah
movb %ah, %cl # CL[7:6] = cyl# (high 2 bits)
# CL[5:0] = sec# = 1
movb %dl, %dh # DH = Head#
movw SECTORS_PER_TRACK, %ax # AL = Num of sectors to read
movb $2, %ah # AH = 02h (Read Disk Sectors)
movb PHYSICAL_DRIVE, %dl # DL = Drive#
int $0x13 # Invoke Disk BIOS
jc error
ret
#
# Local Data
#
last_fat: .long 0xffffffff
last_data: .long 0xffffffff
load_msg: .ascii "Loading "
image_name: .ascii "PREXOS "
crlf: .byte 0x0a, 0x0d
err_msg: .ascii "Error"
.org 510
.word 0xaa55