Submit: Turn in your
spinlock_util.S
, spinlock.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
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
spinlock_util.S
and spinlock.c
will be graded for this assignment.
What happens when multiple cores attempt to transmit output over the same UART at the same time -- without mutual exclusion? In short, Undesirable Behavior™.
Currently, your embedded operating system does not
support mutual exclusion across multiple cores. Refer
to system/testcases.c. In its current state, unparking
cores 1, 2, and 3 to a function that prints "Hello World
from core n"
yields a distorted mess of
characters.
To amend this behavior, you will implement a multicore spinlock for your synchronous serial driver from the previous project.
For this assignment, you will have to complete the spinlock
acquire function, _lock_acquire()
,
in system/spinlock_util.S
. You will also have to
complete lock_acquire()
and lock_release()
in system/spinlock.c
.
Make sure:
serial_lock
in kprintf()
(refer
to include/spinlock.h
).In the coming multicore assignments, you will be running your kernel on the Raspberry Pi 3 Model B+ platform, which has four cores within its ARM Cortex-A53 processor. In previous years, this course used single-core platforms (i.e., PowerPC machines, Linksys MIPS routers, Raspberry Pi 1 boards) to run student code. Since 2019, Marquette has used real multicore hardware (as opposed to virtual machines) to lead hands-on laboratory assignments in Operating Systems. As best we can tell, we are one of the first universities in the world (possibly the first!) to do this successfully as part of a regular graduate course. (You're welcome!)
Your previous UART driver assignment ran on the Pi 3, but you only used one core for that. Now that your embedded O/S will be taking advantage of this multicore architecture, you need to know the multicore hardware operations built in for your use.
Although the concept of unparking is general to multicore
platforms, the specific implementations vary from one platform
to another. When the Pi 3 B+ boots, only core 0 is setup to
run, and cores 1-3 are put in a sleep state. (The startup
sequence in start.S
executes the WFE
opcode, "Wait For Event", on each of the other cores.) To
wake up, or unpark, a core, core 0 sends an event (ARM
opcode SEV
, "Send EVent",) to wake another core.
Upon waking the new core will check the value in a special
predetermined location known as its
mailbox. The core then begins execution at the address
that it finds in its mailbox.
In this example, core 3 is unparked and sent to execute a given
test_procedure()
with no arguments (NULL). See the
"Testing" section below for information about how to use this
function.
Note: unparkcore()
is already
implemented for you and can be found
in system/unparkcore.c
unparkcore(int core, void* address, void* argument);
In the implementation of unparkcore()
, you will
see that it will unpark the core to an assembly routine called
setupCore()
(found
in system/setupCore.S
). setupCore()
is
necessary because the core needs to undergo specific
initializations before any of your code can run.
Note: Source files unparkcore.c
and setupCore.S
are already implemented for you.
First, make a copy of your Project 3 directory:
cp -R xinu-hw3 xinu-hw4
Then, untar the new project files on top of it:
tar xvzf ~brylow/os/Projects/xinu-hw4.tgz
New files:
If you do this correctly, your kprintf.c
synchronous
serial driver file from the previous assignment will remain in place
in the system/
directory. You will not see any output
for the project if a working solution for kgetc()
is not
in place.
As with any programming task, it is important to understand the concepts before jumping into the code. Before beginning this assignment, refer to Chapter 5 of the Dino Book. Make certain you understand the concepts of critical section and mutual exclusion.
Further, please read this
ARM
document explaining LDREX
and STREX
atomic operations which you will use in
your implementation of _lock_acquire()
.
Refer to the testcases()
function
within system/testcases.c
. By default, the testcases
function unparks cores 1, 2, and 3 to a function called
core_print()
. Since core_print()
does
not take parameters,
unparkcore()
passes it NULL arguments.
If the cores print out neatly, then it is highly likely that
your _lock_acquire
function works correctly (and that
you properly called lock_acquire()
and lock_release()
in your kprintf()
function). Note that kprintf()
itself is the
critical section in this assignment that requires mutual
exclusion. It is not necessary to add spinlocks to any of the
other synchronous serial driver functions.
[Revised 2020 Feb 04 20:48 DWB]