 |
COS 318 : Operating system
Fall 2004, Princeton University
Project 1: Bootup Mechanism |
Important Dates
Design Review:
9/20, 7:30-10:30pm
Project Due: 9/27, 11:59pm
Note
about lab computers:
To log on to lab computers and arizona machine, you will need to use
your OIT
username/passwords.
Abstract
The purpose of this project is to write the bootup code for a simple
operating system that we will be developing for the rest of the
semester. Your job is to implement two programs: bootblock.s
and createimage.c. The bootblock is the boot code that
resides on the boot sector and its responsibility is to load the rest
of
the operating system image from other sectors into memory. The createimage
code is a tool (in this case a Linux tool) to create a bootable
operating system image including the bootblock and the rest of the
operating system.
The "bootblock" and "createimage" from this assignment will be used
throughout the semester.
We have provided code for project 1 for you to use as a starting
point. The code can be found on the princeton machines
(arizona.princeton.edu) in either /u/cos318/1_pre/* (or the tarfile /u/cos318/1_pre.tar.
To untar the code type 'tar -xvf 1_pre.tar'). The 1_pre
directory
should contain six files:
- Makefile: A makefile for you to compile with the right
options.
- bootblock_examples.s: A
series of assembly language examples
- bootblock.s: A template for you to write the bootup
code.
- createimage.c: A template for you to write a Linux tool
to create a bootable operating system image on a USB flash disk.
- createimage.given: An executable Linux tool for you to
test your bootblock.s code and validate your createimage.c.
- kernel.s: A minimal kernel to test your bootup code and
your tool.
Since our entire project will be using the USB flash disk, please
do
not read/modify the hard disk.
To sign up for the design review, please use online
signup here.
Project Overview
First, this project requires a design review. For the design
review you are expected to have working print_char and print_string
methods written in assembly and a reasonable design for the rest of the
project. The project itself can be split into two parts, the
bootblock code and the boot image (created by createimage). It is
recommended that you get one part working before attempting the other
part.
A PC can be booted up in two modes: cold boot or warm boot. Cold boot
means you power down and power up the machine by pressing the power
button of a PC. Warm boot means that you press <ctrl><alt><del>
keys together. Either way, the PC will reset the processor and run a
piece of code in the BIOS to try to read the first sector from the
first bootable device. So you should set the computer to try to boot
from the USB flash device if the computer BIOS supports that.
Otherwise,
it will try to
read a sector from other bootable device (say 1st hard disk drive).
Once this sector is read in,
the
CPU will jump to the beginning of the loaded code. In this
project, the bootup code will be in the so called real mode (instead of
protected mode, see links for more information).
Bootblock Overview
The file bootblock.s should
contain the code that will be written on the boot sector of USB flash
disk. This has to be written in x86 assembly and should not exceed 512
bytes (1 sector). This code will be responsible for loading in the rest
of the operating system, setting up a stack for the kernel and then
invoking the kernel. You are required to handle kernels of size
up
to 127 sectors. You will get extra credit if
your
bootblock can handle sizes greater than 128 sectors. (Please read more
about extra credit section if you are interested.)
The bootblock gets loaded at memory address 0x07c0:0000. Your
bootblock should load the OS starting at 0x0000:1000. In real mode, you
have 640 KBytes starting at 0x0000:0000. The low memory area has some
system data like the interrupt vectors and BIOS data. So, to avoid
overwriting them, you should only use memory above 0x0000:1000 for this
assignment.
To design and implement this, you need to learn about x86
architecture, CPU resets and how to read a sector from the USB flash
drive
with BIOS (described below). We have provided you with a trivial and
useless kernel (kernel.s) to test whether your implementation
works.
Note: You can assume that the entire OS is less or equal to
127 sectors for this project. If you are working on your home machines
rather than the lab machines, please make sure your program also run
on lab machine before submit it because your TA will be testing your
program on lab machine.
The links on the main page provide assistance in understanding x86
assembly language and architecture. The precept will also give
assistance in understanding these topics.
To test your bootblock you can use the createimage.given file.
Type './createimage.given --extended ./bootblock ./kernel' then 'cat
image > /dev/sda' to copy the image onto the USB flash disk.
Note: On the lab machines,
the USB flash disk will be detected as /dev/sda. But on other linux
machines you might have, USB flash disk could be detected as /dev/sdb
or even sd? depending on whether other SCSI devices are present in the
system. Please be careful and make sure your USB flash disk is mounted
on /dev/sda before saving the image to /dev/sda. Otherwise, you risk
ruin your file system on your exististing hard disk.
Createimage Overview
This program is a Linux tool to create a bootable kernel image.
To make your life a bit easier, we have provided you with a man
page in the man directory included in the project tarfile. You can view
the man page by typing
man -M man createimage
Please ignore the "-vm" option for this project.
Essentially, createimage takes a bootloader and several executables and
places them all into one image file. The bootloader must be
placed in the first sector, while all other executables should be
placed
at offset specified in the ELF header. To read an ELF file use
fopen, fread, and fseek (see man pages for descriptions). The
program header (figure 2-1 of the
ELF Documentation) contains information about each segment in the ELF
and should be used to determine the address at which to place the
segment. Be aware of the following:
- The length of a segment in the executable file may expand when
loaded into memory.
- Two adjacent segments in an executable file may not be contiguous
when they are loaded into memory, thus you should pad before each
segment.
- When copying segments from executable files into image file, you
should pad after each segment to make sure that its length can be
divided by 512 bytes.
One more thing you will have to do is to mark the USB flash disk as
"bootable device". You will have to put a signature in the boot sector
B(first sector) of the USB flash disk so that the BIOS will recognize
this device as bootable. The signature consists of two bytes: "0x55
0xaa", and you will need to put this signature at the end of the boot
sector at offset 0x1fe.
You should read:
- The man page of createimage, od, and objdump
(see man pages)
- ELF
Documentation (pages 1-1 through 1-7 and 2-1 through 2-6): describe
the image format
Program Submission
Submit your final solution electronically on arizona.princeton.edu
using the following
command:
/u/cos318/bin/318submit 1 README
Makefile bootblock.s createimage.c
The submit command copies your files to the directory /u/cos318/submit/UserLogin/number
and lists all the files that you have submitted for assignment number. UserLogin
is your user account name. If you execute submit after the project
deadline, your files are placed in directory number_late. You
can run submit more than once, and you can submit partial lists of
files.
Each group needs to submit only one copy. This means that only
one of you needs to submit.
Assembly Language Examples
Please read the assembly language provided before reading this
section. The file bootblock_examples.s also contains several x86
assembly language examples.
Loading an segment:offset
The project will require you to load a number into a segment register
to setup the stack and data segments. The code segment register
(CS) cannot be loaded directly, but instead only indirectly through a
JMP type instruction. When loading a value into the stack segment
register (SS), interuppts are disabled for the next instruction, thus
allowing you to set the stack pointer (SP). As an example of
setting up the segment registers for data, consider the following
string copy:
# Setup the registers - see chapter 3 of Intel ISA reference volume 2
movw DATA_SEGMENT, %ax
movw %ax, %ds
movw OTHER_DATA_SEGMENT, %ax
movw %ax, %es
movw STRING_FROM_ADDRESS, %si
movw STRING_TO_ADDRESS, %di
# Move a byte from one string to the other - implictly DS:SI to ES:DI
movsb
# The values in %si and %di are automatically incremented/decremented
based on the DF flag
Display memory
For your design review you are required to implement routines that
print to the screen. During booting, you can write directly to
the
screen by writing to the display RAM which is mapped starting at
0xb800:0000. Each location on the screen requires two bytes---one to
specify the attribute (Use 0x07) and the second for the
character
itself. The text screen is 80x25 characters. So, to write to i-th row and j-th
column, you write the 2 bytes starting at offset ((i-1)*80+(j-1))*2.
So, the following code sequence writes the character 'K' (ascii
0x4b) to the top left corner of the screen.
movw 0xb800,%bx
movw %bx,%es
movw $0x074b,%es:(0x0)
This code sequence is very useful for debugging.
Useful BIOS Features
You are allowed to use these BIOS
functions to complete this assignment.
BIOS Int 0x13 Function 2 (From Undocumented PC)
Reads a number of 512 bytes diskette sectors starting from a specified
location. The data read is placed into RAM at the location specified by
ES:BX. The buffer must be sufficiently large to hold the data AND must
not cross a 64K linear address boundary. (For our project, for
simplicity, you can assume cylinder number bits 6&7 is zero).
Called with:
ah = 2
al = number of sectors to read,
ch = cylinder number, (lowest 8 bits of the 10-bit cylinder
number, 0 based)
cl, bits 6&7 = cylinder number bits 8 and 9.
bits 0-5 = starting sector number, 1 to 63
dh = starting head number, 0 to 255
dl = drive number, (0x80 + zero based hard driver number)
(Note: use dl = 0x80 for the first hard disk. For this project, if you
want to boot USB flash disk on the lab machines, you can set dl to be
0x80 because on our lab machines, the USB flash disk will be treated as
the first bootable hard disk with dl=0x80. On other machines, this
might not be true since different BIOS could treat the USB flash disk
as different devices like floppy disk, see note below for
more information.)
es:bx = pointer where to place information read from diskette
Returns:
ah = return status (0 if successful)
al = burst error length if ah = 11; undefined on most BIOSes for
other ah return status values.
carry = 0 successful, = 1 if error occurred
Note: If you want to make your USB flash disk to
boot on machines other than the lab machines, you may also need to save
the register %dl at the begining of your bootloader code. The register
%dl will contain the correct driver number corresponding to this USB
boot device. (Because on different computer, the USB flash disk could
be treated differently. We have seen on one computer with AMIBIOS, the
USB boot device is being recognized as driver 0x0 rather than 0x80 on
the lab machines.) So you can use the right dl value when you call the
BIOS function calls.
BIOS Int 0x10 Function 0x0e (From Undocumented PC)
This function sends a character to the display at the current cursor
position on the active display page. As necessary, it automatically
wraps lines, scrolls and interprets some control characters for
specific
actions. Note : Linefeed is 0x0a and carriage return is 0x0d.
Called with:
ah = 0x0e
al = character to write
bh = active page number (Use 0x00)
bl = foreground color (graphics mode only) (Use 0x02)
Returns:
character displayed
Extra credits
In order to get extra credits, your
bootloader will need to be able to load more than 128 sectors. In order
to do that, you will need to use another BIOS call listed below. With
that BIOS call, you will be able to get the disk parameter and then
read sector by sector to load more than 128 sectors.
BIOS Int 0x13 Function 8 (From
Undocumented PC)
This function gets the drive parameters for the specified driver.
Called with:
ah = 8
dl = driver number (0x80 + zero based hard driver number)
returns:
if successful:
ah = 0
al = undefined
carry = 0
ch = maximum cylinder number (lowest 8 bits of the 10-bits cylinder
number, 0 based)
cl, bits 6&7 = cylinder number bits 8 and 9.
bits 0-5 = maximum sector number, 1 to 63
dh = maximum head number, 0 to 255
dl = number of drives
if failed (but at least one drive is active)
ah = 7
carry = 1
al = cx = dx = 0
if failed (because no hard disk exists)
ah = 1
carry = 1
al = bx = cx = dx = es = 0
COS318 Staff