Submit: Turn in your
dispatch.c
and
testcases.c
source files using the
turnin command on morbius.mscsnet.mu.edu or
one of the other
Systems Lab machines.
Work should be completed in pairs. Be certain
to include both names in the comment block at the top of all
submitted source code files. It would be courteous to confirm with your
partner when submitting the assignment. You may modify any files
in the operating system, but only changes to
dispatch.c
and testcases.c
will be graded for this assignment.
First, make a copy of your Project 4 directory:
cp -R
xinu-hw4 xinu-hw5
Then, untar the new project files on top of
it:
tar xvzf ~brylow/os/Projects/xinu-hw5.tgz
New files:
system/start.S Updated O/S start with exception handlers in place. system/interrupt.S RISC-V assembly code for managing exceptions -- calls dispatch(). system/xtrap.c Default Xinu Trap Handler catches exceptions. system/dispatch.c Dispatcher for interrupts and exceptions, called from interrupt.S. system/syscall_dispatch.c A simple system call interface, plus stubs for several simple user and kernel syscall components. You must call this from dispatch(). include/syscall.h New header with syscall definitions, structures and prototypes.
The dispatch() currently function defaults to treating the ecall
(environment call) instruction as a generic, show-stopping hardware
exception. You must add the intelligence to recognize when the exception
is caused by a deliberate System Call from a "user" process in to the
"kernel", and collect the system state information necessary for
syscall_dispatch() to do its work.
To begin, we'll start with just five simple syscalls:
user_none()
- A dummy syscall that goes to the O/S kernel,
does nothing, and comes back.
user_yield()
- Allows the current running process to yield the
processor, inviting the O/S to reschedule another process to run, if
conditions permit.
user_getc()
- Fetches a character from a hardware input
device. By default, device '0' will be your existing synchronous serial
port driver.
user_putc()
- Writes a character to a hardware output device.
By default, device '0' will be your existing synchronous serial port
driver.
user_kill()
- Kills the current running process
It's important to understand the various privilege levels a RISC-V processor can be in. Different privilege levels dictate which control and status registers can be accessed. RISC-V has three different privilege levels (listed from highest to lowest privilege).
ecall
instruction. Once an ecall
instruction is executed, the processor automatically elevates to the next higher
privilege level and runs that privilege's trap handler. Once that mode is done servicing the trap, it executes an mret
or sret
(depending on the mode) instruction to return to the privilege mode it was previously in and continue where it left off.
Once you believe your've finished your trap handler, you will need to make modifications to ctxsw.S
and create.c
files. In ctxsw.S
, you need remove the jalr
line and replace it with
ld t0, PREG_PC*8(**REGISTER THAT HOLDS newregs ADDRESS**) beq t0, ra, switch li t4, SSTATUS_S_MODE csrc sstatus, t4 csrw sepc, t0 sret switch: retThis section of code switches from S-Mode to U-Mode if the process is running the first time. All other times, the process must have got to ctxsw from a trap, thus we will let the trap handler switch modes when it returns. Since
kill()
is a kernel mode function (which should not be directly called by user processes), you must change the kill(currpid);
line in create.c
to user_kill();
Our current simple embedded O/S does not yet provide memory
protection. As a result, our system call interface
will only provide the first of required components for protecting the
O/S from user processes. Your user processes will still be able to call
most arbitrary internal kernel functions without going through the system call
interface, and read or write data from other processes or the O/S itself. Since
ctxsw
has a special instruction (sret
) that can only be run from S-Mode,
only processes running in S-Mode (like the init process) can run ctxsw. If you
get an Illegal Instruction exception, that means you're calling functions from
a mode that you don't have the privilege to call.
As we add more features to our O/S in weeks to come, we will create the
memory virtualization abstractions that will prevent user code from
accessing O/S functions and data without going through an approved
user_*
system call function. For now, we're building system
calls even though all of our processes are still executing at the "kernel"
privilege level, with unrestricted view of all memory.
The default test cases provided with the tarball exercise each one of our
starting system calls at least once. Judicious use of additional debugging
kprintf()
calls will be essential for debugging your system
call interface as you build it. Be certain to comment out these debugging
statements when you turn in your code each night, lest TA-Bot find your
code unworthy.
Once you have the basics working, consider filling in some of the other
system call stubs left for future expansion, or write a version of
user_printf()
that processes all of the format string in
the user process, but relies on the user_putc()
syscall for
requesting that the O/S output each resulting character.
This project can be completed in fewer than a dozen lines of code, but the technical details of the system call can be quite complex and hard to keep straight. It is very unlikely that random code snippets from the Internet will help to solve this one -- it is essential that you understand the data structures and mechanisms involved, and reason through an adequate design to solve the problem.
[Revised 2023 Feb 22 02:42 DWB]