Up to now, you haven't needed the code for the operating system. We've just been building and testing a machine that could support an OS. This is the point at which we make the transition.
...\cebollita\apps
should already contain a file named
image
That's an OS disk image file, , including the OS and some test applications.
However, if you want to re-build it,
type make image
in directory ...\cebollita\apps
.
That will also produce a file named image.map
that tells you the
disk block number each is loaded at (which you need to know to use the shell).
If you want to change the set of applications in the image you could try
editing the Makefile
: look for the line (near the top)
IMAGE_USERAPPS = tests/helloworld.exe ...
and add or
delete what you want.
Try running the image. In Cebollita, while in directory apps
:
./cebrun.sh image
Because we're getting perilously close to what a real machine and system is at this point, you do not simply use the SMOK memory component to magically read the OS disk image into itself. Instead, you use SMOK's boot capability, which requires you to attach a virtual disk (i.e., the OS image file you generated) to the Block Controller device inside the memory map container. Having done that, you can use the power on toolbar button to boot your machine. (See the documentation on SMOK's Block Controller for more information.)
You also do not Run your machine, you Power On (boot) it.
There is a (different) toobar button for that.
When you boot your machine, the SMOK Block Controller will load the boot
loader (whose source is .../cebollita/apps/os/bootloader.s
)
into memory at address 0.
When that is done, your machine starts executing (so
it should have its PC initialized to 0).
The boot loader loads the rest of the OS into memory and then
transfers control to it.
Once the OS has initialized itself it starts the first process, a shell.
The shell is just a regular old user application
(.../cebollita/apps/os/shell.c
).
The OS transfers control to it, and it starts running.
This is a very primitive system -- it has no file system at all. (It has a disk, but it doesn't have any notion of files, just raw disk space.) All the shell can do for you is start other programs.
The shell sits in a loop.
At the top of the loop it prints a prompt asking you for a disk block number.
To identify the program you want to run, you have to give the disk block number at which it is
located. The block number corresponding to each program on your disk is printed out when you
create the disk image, so those are the numbers you need to supply.
They are also dumped to the file .../cebollita/apps/image.map
when
you build the image
file
(see the first section of this writeup).
.../cebollita/apps/os/
.
bootloader.s
is the boot loader.
When the machine (or simulator) is powered on, the hardware loads the
first block (512 bytes) of the disk into memory at location 0. The
bootloader is those first bytes on the disk. It (a) sits in a loop
moving itself to a higher memory address, and then (b)loads the remainder
of the OS from the disk into memory, starting at location 0
(which is why it has to move itself). The boot loader is not linked with
the OS -- it's a
stand-alone program that sets things up to the get the OS going.
The OS is in files trap-handler.s
and os.c
.
The former is a small set of routines that need to be written in
assembler. For example, there is a routine to save all the registers
on entry into the OS, and another one to restore them on return from
the OS.
os.c
is the bulk of the OS. Once it gets going, it's
entered at exceptionHandler()
. Entries into the OS are
not procedure calls -- the OS doesn't do a "return"
to go back to the application. Instead, the trap handler has saved
the application's registers on entry. dispatch()
restores them, and sets the PC to the EPC. Voila, you're back in the
application.
The OS has a $gp and $sp, just like anyone else. To preserve them across dispatches of applications, it saves them in memory locations 0 and 4. When control comes back to the OS, the trap handler retrieves them from there, and then the OS can run as normal C code again.
On boot, the OS initializes itself and then launches the first
program, a shell. This shell is very primitive. You type a disk
block number and it tries to launch a new process, assuming that the
block you gave was the first block of an executable stored on disk.
The standard built of the image
file loads
the disk with six applications: the shell, a helloworld application,
a quicksort application, a character IO test, a syscall test, and
a program that generates an addressing exception.
(Yes, you can launch a shell process from the shell.)
You can terminate a shell's execution by giving a
negative number for the disk block. If it is the original shell,
well, the OS crashes. That's intentional.
What you type into the shell is not echo'ed back to you. Ever.