Great blogpost. In general, I find Hubris really elegant and beautiful.
The first thing I like is the isolation in architecture. To quote Cliff, Hubris has three points where it kind of unusual in its class, compare to other OSes:
components are separately compiled and isolated from one another using the processor’s memory protection hardware. We refer to these isolated components as tasks.
drivers run in unprivileged mode, outside the kernel, and are typically isolated in their own tasks.
Hubris is an aggressively static system. The configuration file defines the full set of tasks that may ever be running in the application. These tasks are assigned to sections of address space by the build system, and they will forever occupy those sections.
The second thing I like, is the use of Rust memory safety properties. Quote:
_Because of this, we don’t need to restrict how each task is written under the hood. While most of Hubris itself and our application code is written in Rust’s safe subset, which grants immunity from common classes of memory usage errors, the unsafe subset of Rust is incredibly valuable, particularly when writing fast drivers, or doing things like DMA. While code using unsafe Rust is still significantly safer than C – for instance, array indexing is still checked and integer overflows are still caught – it has access to operations that suppress those checks, and so it has the ability to corrupt any memory it can reach if it works hard enough. The combination of encouraging (but not requiring) safe Rust, while using memory isolation as a backstop, gives us a lot more flexibility without any loss of system-level safety.
Which is not to say things never go wrong – just that, when they do, it is not typically a memory safety issue. It is far more likely to be a misread of a component datasheet, a subtle interaction between two tasks, or a plain-old logic error. In these cases, we break out Humility._
I’m also excited about how Hubris relies on Rust’s references and borrowing for across task boundaries: _In Rust, references represent borrowed data, which comes from somewhere else, possibly your caller’s stack. References are analyzed, at compile time, by the compiler to check that code that operates on borrowed data will stop operating on it before returning. In general, it’s legal to pass references to borrowed data down into functions you call, and then operate on it after the function has returned, because you can rely on the compiler to ensure that that function hasn’t secretly hung onto a reference.
The Hubris lease mechanism extends this function-to-function borrowing mechanism across task boundaries._
the Memory Protection Unit. This piece of hardware in many ARM and RISC-V chips allows the kernel to lock down whether various segments of memory are readable, writable, and executable.
To clarify, is this the bona-fide MMU, with S-mode/EL1 fine-grained page tables/trees? Or is this the PMP (not sure of the arm term) with M-mode/EL3 coars(er)-grained memory ranges?
And see also: tock, also written in rust and using PMP-style memory protection.
Great blogpost. In general, I find Hubris really elegant and beautiful.
The first thing I like is the isolation in architecture. To quote Cliff, Hubris has three points where it kind of unusual in its class, compare to other OSes:
components are separately compiled and isolated from one another using the processor’s memory protection hardware. We refer to these isolated components as tasks.
drivers run in unprivileged mode, outside the kernel, and are typically isolated in their own tasks.
Hubris is an aggressively static system. The configuration file defines the full set of tasks that may ever be running in the application. These tasks are assigned to sections of address space by the build system, and they will forever occupy those sections.
The second thing I like, is the use of Rust memory safety properties. Quote:
_Because of this, we don’t need to restrict how each task is written under the hood. While most of Hubris itself and our application code is written in Rust’s safe subset, which grants immunity from common classes of memory usage errors, the unsafe subset of Rust is incredibly valuable, particularly when writing fast drivers, or doing things like DMA. While code using unsafe Rust is still significantly safer than C – for instance, array indexing is still checked and integer overflows are still caught – it has access to operations that suppress those checks, and so it has the ability to corrupt any memory it can reach if it works hard enough. The combination of encouraging (but not requiring) safe Rust, while using memory isolation as a backstop, gives us a lot more flexibility without any loss of system-level safety.
Which is not to say things never go wrong – just that, when they do, it is not typically a memory safety issue. It is far more likely to be a misread of a component datasheet, a subtle interaction between two tasks, or a plain-old logic error. In these cases, we break out Humility._
I’m also excited about how Hubris relies on Rust’s references and borrowing for across task boundaries: _In Rust, references represent borrowed data, which comes from somewhere else, possibly your caller’s stack. References are analyzed, at compile time, by the compiler to check that code that operates on borrowed data will stop operating on it before returning. In general, it’s legal to pass references to borrowed data down into functions you call, and then operate on it after the function has returned, because you can rely on the compiler to ensure that that function hasn’t secretly hung onto a reference.
The Hubris lease mechanism extends this function-to-function borrowing mechanism across task boundaries._
A lot of the text was copied from here: http://cliffle.com/blog/on-hubris-and-humility/
To clarify, is this the bona-fide MMU, with S-mode/EL1 fine-grained page tables/trees? Or is this the PMP (not sure of the arm term) with M-mode/EL3 coars(er)-grained memory ranges?
And see also: tock, also written in rust and using PMP-style memory protection.
Not an MMU, just a memory access control mechanism.
https://developer.arm.com/documentation/ddi0439/b/Memory-Protection-Unit/About-the-MPU?lang=en
OK, so the second style.
Great article, this makes me want to go out and buy a PineWatch! Too bad they’re out of stock :(
Great job artemis!