Derust

OS in Rust

Announcements

  • Welcome to OS in Rust
    • The most famous systems topic…
    • Now in Rust, your favorite language!
  • Action Items:
    • Access the course webpage
    • Join the Discord!
      • We’ll use CSSA.
    • Set up Linux/Rust/Git.

Homework

  • The first homework, wc, is ready now.
    • Mostly just Rust practice.
  • Due Friday, 23 Jan at 1440 PT.
    • Friday of next week.

Today

  • Logistics
  • On Systems
  • On Rust
  • Course Sketch
  • Derust

Logistics

It me

  • Name
    • (Prof. )?Calvin
  • Credentials
    • B.A. Mathematics
    • B.S., M.S., Ph.D. Computer Science
  • Pronouns
    • they/them

The Great Work

  • Thesis Title
    • Mining Secure Behavior of Hardware Designs
  • Plain English
    • Just as there are bugs in code that makes software, modern hardware is also written in code and therefore may contain bugs. I find these bugs.

Course Format

  • Lecture Monday
  • Lab Wednesday
  • Homework due Friday 1440 PT (the next Friday)
    • You should all meet on Friday.
  • In-class midterm week of 3/16
  • Your OS is due whenever the final is scheduled.
    • Waiting on registrar to announce.

Grade Policy

  • Your voices have been heard:

I think failing my systems class was the best thing that happened to me.

This course demanded strong self-motivation… Thus, I didn’t learn as much as I might’ve if grading mattered more.

Specification Grading

  • Basically, I will assign some stuff but you don’t have to do all of it perfectly.
    • No partial credit, everything is boolean grading.
  • I took distributions over final, midterm, and Lab/HW

It pays to get A’s

Grade Requirements
A Final meets spec
A 90% ave HW/Lab/Midterm, Final compiles
A- 90% ave HW/Lab, Midterm/Final compile
A- Midterm meets spec, Final compiles

But B’s are the Knees

Grade Requirements
B+ 90% ave HW/Lab, Midterm compiles
B 80% ave HW/Lab, Midterm compiles
B- 80% ave HW/Lab

These may (???) earn degrees

Grade Requirements
C 80% ave Lab
D 60% ave Lab
F Anything else

Lab Grading

  • “A” (=100%)
    • Meets spec by end of class
  • “B” (=90%)
    • Compiles at end of class
  • “F” (=0%)
    • Anything else

HW Grading

  • “A” (=100%)
    • On time
    • Passes autograder.
      • I lack confidence I can make excellent autograders for all stages, so give me some grace here.
  • “B” (=90%)
    • On time
    • Compiles
  • “F” (=0%)
    • Anything else

Final

  • Create, document, and publish an operating system… in Rust!
    • Free-standing binary
    • Faults/exceptions/interrupts
    • Memory management

Late Work Policy

  • Late work is not accepted.
    • No exceptions.
    • The specification grading framework incorpates tolerances.
    • An OS that meets spec by end-of-term is an A, always.

AI Policy

  • I would be shocked if it is helpful to you.
  • If you think it is helpful, that is probably not a good sign.
  • You can use it (I wouldn’t)

Collaboration Policy

  • You should chat with each other.
  • You should write your own code.
  • If someone shows you something and you don’t understand it as well as code you write yourself, it is a waste of your time to use the code.
    • But not a waste as a way to learn.

Sections

a la 151

  • This term we will have sections
  • Meet Friday 1310-1440
  • Meant to help unpack learning in class and work on homework.
  • Single section/section-leader (Jimmy)
  • Miss more than 4 sections and drop one “bin” on specification grading.
    • Unless excused.

On Systems

The Hard Part

  • After ~10 years of systems research I’ve convinced myself only two things really matter:

    • References, and
    • Recursion
  • Recursion isn’t too bad…

>>> exp = lambda b, n : b if n == 1 else b*exp(b,n-1)
>>> exp(2,8)
256
  • Covered in 271 and possibly 351 and no longer interesting on its own, though will be used.

References

  • References are a beast, but without them nothing makes sense!
>>> x = 1
>>> def addx():
...     x += 1
...
>>> addx()
UnboundLocalError:
<blah blah blah error messages>
>>> x = [1]
>>> def addx():
...     x[0] += 1
...
>>> addx()
>>> x
[2]

Insight

  • Definition:
    • References: Values that are meaningful only as keys to values in key-value memory storage, as a memory addresses.
  • Importance:
    • This key-value storage is a service provided by the operating system!

Python and OSes

  • Why not .py (.js, .java, .cs, .cpp, etc)?
    • These languages assume an underlying OS (or VM or browser).
    • There is more to life than running code other people wrote.
    • Good languages (except Java) but not for us
    • The other systems language is C (and maybe C++)

On Rust

Rust and References

  • Rust:
    • Obscures references, but…
    • Does not obscure memory.
  • This is good!
    • Memory matters, but pointers are historical accident.
  • You should probably use Rust in every application where correctness or performance matters.

Rust and Recursion

  • Separately, Rust is built on one of the most exciting ongoing computer science research efforts:
  • LLVM (stands for LLVM)
  • Basically, a way of turning human-readable code into machine-executable code.
    • Very good at turning recursion into iteration and/or vice versa.
  • This term, we benefit from LLVM in that we can make Rust programs for platforms without an underlying OS.

Course Sketch

Citation

  • We will follow “Philipp Oppermann’s blog” “Writing an OS in Rust” version 2.
  • Read more.

Bare Bones

  • A binary that doesn’t require an underlying OS.
  • Something that (1) runs and (2) prints.
  • Simple I/O
  • Unit and integration testing.

Interrupts

  • CPU Exceptions
    • Like division by zero.
  • Double faults
    • Faults while handling a fault.
  • Hardware interrupts
    • Like I/O

Memory Management

  • Paging
  • Page tables
    • Together, how to find things you saved for a while.
  • The heap
  • Heap allocation.
    • Together, how to find things you saved while working.

Final

Final Exam/Project

  • Implement Minimal OS in Rust
    • Individual
    • Under version control
    • Compiles and runs and is tested

On vim

Vim

  • You should use vim or another console-based editor as a component of your learning in this class.
  • This will not be assessed (how can it be) but will likely be expected of you in life.
  • I will live-code in vim probably.
    • This will be painful, but that is okay.

Derust

Rust Features

Options

  • Sometimes code makes sense.
  • MAD
from numpy import uint64

# Multiply-add operation
def mad(a:uint64, b:uint64, c:uint64) -> uint64:
    return a + b * c

Options

  • Sometimes it doesn’t.
  • This needn’t return a positive value!
from numpy import uint64

# Multiply-sub operation
def msu(a:uint64, b:uint64, c:uint64) -> uint64:
    return a - b * c

Options

  • Handle your cases!
  • But this causes other problems…
from numpy import uint64

# Multiply-sub operation
def msu(a:uint64, b:uint64, c:uint64) -> uint64:
    if b * c > a:
        return a + b * c
    else:
        return None

Options

  • Handle your cases!
  • But this causes other problems…
from numpy import uint64
from typing import Union

# Multiply-sub operation
def msu(a:uint64, b:uint64, c:uint64) -> Union[None,uint64]:
    if b * c > a:
        return a + b * c
    else:
        return None

Options

  • Ever wish there was a nice way to handle a Union[None,x] \(\forall x\)?
  • It’s the option type:
fn divide(numerator: f64, denominator: f64) -> Option<f64> {
    if denominator == 0.0 {
        None
    } else {
        Some(numerator / denominator)
    }
}

Rust Features

Results

  • Results are basically options that incorporate error handling.
// Linux Kernel Versions, latest is 6
fn check_version(val: u8) -> Result<u8, &'static str> {
    if val < 7 {
        return Ok(val);
    } else {
        return Err("Not yet released.")
    }
}

Check it

  • Add main.
src/main.rs
fn main() {
    dbg!(check_version(4));
    dbg!(check_version(7));
}
  • cargo r
[src/main.rs:10:5] check_version(4) = Ok(
    4,
)
[src/main.rs:11:5] check_version(7) = Err(
    "Not yet released.",
)

Rust Features

std

  • Options, results, and more fun things, like vectors, are all part of std

The Rust Standard Library is the foundation of portable Rust software, a set of minimal and battle-tested shared abstractions for the broader Rust ecosystem. It offers core types, like Vec<T> and Option<T>, library-defined operations on language primitives, standard macros, I/O and multithreading, among many other things.

Iterators

  • An iterator is pretty much a for-each loop, the Python loop type.
let v1 = vec![1, 2, 3];

let v1_iter = v1.iter();

for val in v1_iter {
    println!("Got: {val}");
}
  • Oh look, vec! from std

Closures

  • A closure is pretty much a lambda function, like the Python lambda keyword.
fn  add_one_v1   (x: u32) -> u32 { x + 1 }
let add_one_v2 = |x: u32| -> u32 { x + 1 };
let add_one_v3 = |x|             { x + 1 };
let add_one_v4 = |x|               x + 1  ;
  • No one can stop you from writing fn’s, not even people who actually know Rust!

Pattern Matching

  • The language designers intend options (and results) to be handled as follows:
fn main() {
    let s = "Hello, world";
    match s.chars().nth(11) {
        Some(c) => println!("The 11th character is {:?}", c),
        None => println!("String `s` is fewer than 11 characters in length"),
    }
}
  • Basically, if-else is “un-Rust-like” most of the time.

Formatting

  • print!, println!, and format! are all nice ways to work with strings.
  • Handy if you are an OS and want to interface with a human.
  • Assuming you (the OS) know how to display text.
    • We’ll get there.
format!("test");                             // => "test"
format!("hello {}", "world!");               // => "hello world!"
format!("x = {}, y = {val}", 10, val = 30);  // => "x = 10, y = 30"
let (x, y) = (1, 2);
format!("{x} + {y} = 3");                    // => "1 + 2 = 3"
  • Similar to Python f-strings and also every other language.

Read More

Rust Features

Ownership

  • Big topic.
  • Big deal.
  • Learn by doing.
  • Quick example.

Size

  • Versus e.g. u8 which was a fixed size.
  • A string is not.
// String of length 0
let s = ""; 
// Complete text of Bhagavad Gita
let t = "Dhritarashtra said: O Sanjay, after gathering on the holy field of Kurukshetra, and desiring to fight, what did my sons and the sons of Pandu do? ...
  • And in fact, if we implemented Python integers in Rust, they would follow similar rules.
    • Anything that has no bounded upper size, basically.

Ownership Rules

  • Each value in Rust has an owner.
  • There can only be one owner at a time.
  • When the owner goes out of scope, the value will be dropped.

Scope

  • Quoth Rust Book:
{                      // s is not valid here, since it's not yet declared
    let s = "hello";   // s is valid from this point forward

    // do stuff with s
}                      // this scope is now over, and s is no longer valid
  • You can experimentally verify each of the claims.
  • Basically, the memory associated with s exists at some time points, but not others.
  • Think: Address of building that hasn’t been made/has been condemned.

Capital S String

  • Capital S String is closer to data structure than a data type in some ways:
    let mut s = String::from("hello");

    s.push_str(", world!"); // push_str() appends a literal to a String

    println!("{s}"); // this will print `hello, world!`
  • It accepts a push operation a la a queue/stack/list, and must be initialized with a function call.
  • A String is mutable, a literal is not.

It’s confusing

  • This leads to some un-Pythonic behavior.
  • We compare fixed size and variable size types, which have distinct behavior
  • This works.
fn main() {
    let x = 5;
    let y = x;
    println!("{x}");
}
  • This doesn’t.
fn main() {
    let s = String::from("6");
    let t = s;
    println!("{s}")
}

What’s happening?

  • s passes “out of scope” once its value is assigned to t.
  • Thereafter, there is no declared variable of name s.
  • But this only happens to some types.
    • It is unbounded types, but it’s unclear that e.g. String::from("6") isn’t a fixed width string of lenth 1 (to me at least).

Clone

  • My secret inside source with a real job (e.g. not professor) who writes Rust and also thought this was a bit silly:

Whenever I use Rust, I just always .clone and when someone asks me about it, I say that’s a performance optimization for latter.

  • Rust says the same thing, actually:
help: consider cloning the value if the performance cost is acceptable
  |
6 |     let t = s.clone();
  |              ++++++++

Example

  • It is reasonable to use .clone on capital S String for e.g. Wordle, as needed.
  • As a challenge, don’t use .clone (you don’t need it)
  • This works.
fn main() {
    let x = 5;
    let y = x;
    println!("{x}");
}
  • This works.
fn main() {
    let s = String::from("6");
    let t = s.clone();
    println!("{s}")
}

Today

Fin