Project #4: Spinlock

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.

Multicore Serial Driver

Objective

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:

Background

Multicore Raspberry Pi 3 B+

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.

 

Pi 3 processor image

Unparkcore()

start sequence + unpark core

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.

Preparation

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().

Testing

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.


[back]

[Revised 2020 Feb 04 20:48 DWB]