Compare commits
No commits in common. "909798eb05db578082e7a86672efd30157cdf698" and "a6013bef3e5bec0d3269e19e5cabcb9e94ceaee5" have entirely different histories.
909798eb05
...
a6013bef3e
9 changed files with 156 additions and 624 deletions
137
assets/style.css
137
assets/style.css
|
@ -1,122 +1,87 @@
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--fg: #cdd6f4;
|
|
||||||
--bg: #11111b;
|
|
||||||
--link: #bd93f9;
|
|
||||||
--overlay: #313244;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skip {
|
.skip {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: -10000px;
|
left: -10000px;
|
||||||
top: auto;
|
top: auto;
|
||||||
width: 1px;
|
width: 1px;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.skip:focus {
|
.skip:focus {
|
||||||
position: static;
|
position: static;
|
||||||
width: auto;
|
width: auto;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
max-width: 50rem;
|
max-width: 45rem;
|
||||||
padding: 2rem;
|
padding: 0 2rem;
|
||||||
margin: 0 auto;
|
margin: 2rem auto 0;
|
||||||
line-height: 1.8;
|
line-height: 1.6;
|
||||||
background: var(--bg);
|
background: #11111b;
|
||||||
color: var(--fg);
|
color: white;
|
||||||
font-family: serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,
|
h1, h2, h3 {
|
||||||
h2,
|
margin-bottom: 0.25em;
|
||||||
h3,
|
line-height: 1.3;
|
||||||
h4 {
|
|
||||||
margin-block-end: 0.25em;
|
|
||||||
line-height: 1.3;
|
|
||||||
font-family: sans-serif;
|
|
||||||
|
|
||||||
a,
|
|
||||||
a:visited {
|
|
||||||
color: var(--fg);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: var(--link);
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
margin-block-start: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin-block-start: 0;
|
margin-top: 0;
|
||||||
text-align: justify;
|
}
|
||||||
hyphens: auto;
|
|
||||||
|
p + p {
|
||||||
|
text-indent: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#main-nav {
|
#main-nav {
|
||||||
line-height: 1.3;
|
font-size: 125%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#main-nav ul {
|
#main-nav ul {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0 0 1em;
|
margin: 0 0 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#main-nav li {
|
#main-nav li {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-inline-end: 0.5em;
|
}
|
||||||
|
|
||||||
|
#main-nav li::before {
|
||||||
|
content: '/ ';
|
||||||
|
}
|
||||||
|
|
||||||
|
#main-nav li:first-child::before {
|
||||||
|
content: '';
|
||||||
}
|
}
|
||||||
|
|
||||||
#site-title {
|
#site-title {
|
||||||
font-family: sans-serif;
|
font-weight: bold;
|
||||||
font-weight: bold;
|
color: inherit;
|
||||||
font-size: 150%;
|
text-decoration: none;
|
||||||
margin-bottom: 0.25rem;
|
}
|
||||||
|
|
||||||
|
#site-title:hover {
|
||||||
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
margin: 2em 0 0;
|
margin: 2em 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
header h1 {
|
header h1 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
a,
|
a, a:visited {
|
||||||
a:visited {
|
color: #8ef;
|
||||||
color: var(--link);
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
border: none;
|
|
||||||
height: 1px;
|
|
||||||
background: var(--overlay);
|
|
||||||
}
|
|
||||||
|
|
||||||
pre code {
|
|
||||||
background: black;
|
|
||||||
border: 1px solid var(--overlay);
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 1em;
|
|
||||||
display: block;
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
font-size: 125%;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
:root {
|
:root {
|
||||||
--chroma-bg: #111;
|
--chroma-bg: #111;
|
||||||
--comment: #9e9e9e;
|
--comment: #9e9e9e;
|
||||||
--cyan: #8be9fd;
|
--cyan: #8be9fd;
|
||||||
--green: #50fa7b;
|
--green: #50fa7b;
|
||||||
--orange: #ffb86c;
|
--orange: #ffb86c;
|
||||||
--pink: #ff79c6;
|
--pink: #f5a;
|
||||||
--purple: #bd93f9;
|
--purple: #bd93f9;
|
||||||
--red: #f55;
|
--red: #f55;
|
||||||
--yellow: #f1fa8c;
|
--yellow: #f1fa8c;
|
||||||
}
|
}
|
||||||
/* @media (prefers-color-scheme: light) {
|
/* @media (prefers-color-scheme: light) {
|
||||||
:root {
|
:root {
|
||||||
--chroma-bg: #eee;
|
--chroma-bg: #eee;
|
||||||
--comment: #666;
|
--comment: #666;
|
||||||
--cyan: #04a5e5;
|
--cyan: #04a5e5;
|
||||||
--green: #40a02b;
|
--green: #40a02b;
|
||||||
--orange: #fe640b;
|
--orange: #fe640b;
|
||||||
--pink: #ea76cb;
|
--pink: #ea76cb;
|
||||||
|
@ -22,134 +22,49 @@
|
||||||
--yellow: #df8e1d;
|
--yellow: #df8e1d;
|
||||||
}
|
}
|
||||||
} */
|
} */
|
||||||
/* Background */
|
/* Background */ .bg,
|
||||||
.bg,
|
/* PreWrapper */ .chroma { background-color:var(--chroma-bg); }
|
||||||
/* PreWrapper */ .chroma {
|
/* Other */ .chroma .x { }
|
||||||
background-color: var(--chroma-bg);
|
/* Error */ .chroma .err { }
|
||||||
}
|
/* CodeLine */ .chroma .cl { }
|
||||||
/* Other */
|
/* LineLink */ .chroma .lnlinks { outline:none;text-decoration:none;color:inherit }
|
||||||
.chroma .x {
|
/* LineTableTD */ .chroma .lntd { vertical-align:top;padding:0;margin:0;border:0; }
|
||||||
}
|
/* LineTable */ .chroma .lntable { border-spacing:0;padding:0;margin:0;border:0; }
|
||||||
/* Error */
|
/* LineHighlight */ .chroma .hl { background-color:#3d3f4a }
|
||||||
.chroma .err {
|
/* LineNumbersTable */ .chroma .lnt,
|
||||||
}
|
/* LineNumbers */ .chroma .ln { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:var(--comment) }
|
||||||
/* CodeLine */
|
/* Line */ .chroma .line { display:flex; }
|
||||||
.chroma .cl {
|
/* Keyword */ .chroma .k,
|
||||||
}
|
|
||||||
/* LineLink */
|
|
||||||
.chroma .lnlinks {
|
|
||||||
outline: none;
|
|
||||||
text-decoration: none;
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
/* LineTableTD */
|
|
||||||
.chroma .lntd {
|
|
||||||
vertical-align: top;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
/* LineTable */
|
|
||||||
.chroma .lntable {
|
|
||||||
border-spacing: 0;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
/* LineHighlight */
|
|
||||||
.chroma .hl {
|
|
||||||
background-color: #3d3f4a;
|
|
||||||
}
|
|
||||||
/* LineNumbersTable */
|
|
||||||
.chroma .lnt,
|
|
||||||
/* LineNumbers */ .chroma .ln {
|
|
||||||
white-space: pre;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
margin-right: 0.4em;
|
|
||||||
padding: 0 0.4em 0 0.4em;
|
|
||||||
color: var(--comment);
|
|
||||||
}
|
|
||||||
/* Line */
|
|
||||||
.chroma .line {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
/* Keyword */
|
|
||||||
.chroma .k,
|
|
||||||
/* KeywordConstant */ .chroma .kc,
|
/* KeywordConstant */ .chroma .kc,
|
||||||
/* KeywordNamespace */ .chroma .kn,
|
/* KeywordNamespace */ .chroma .kn,
|
||||||
/* KeywordPseudo */ .chroma .kp,
|
/* KeywordPseudo */ .chroma .kp,
|
||||||
/* KeywordReserved */ .chroma .kr {
|
/* KeywordReserved */ .chroma .kr { color:var(--pink) }
|
||||||
color: var(--pink);
|
/* KeywordType */ .chroma .kt { color:var(--cyan) }
|
||||||
}
|
/* KeywordDeclaration */ .chroma .kd { color:var(--cyan);font-style:italic }
|
||||||
/* KeywordType */
|
/* Name */ .chroma .n { }
|
||||||
.chroma .kt {
|
/* NameBuiltinPseudo */ .chroma .bp { }
|
||||||
color: var(--cyan);
|
/* NameConstant */ .chroma .no { }
|
||||||
}
|
/* NameDecorator */ .chroma .nd { }
|
||||||
/* KeywordDeclaration */
|
/* NameEntity */ .chroma .ni { }
|
||||||
.chroma .kd {
|
/* NameException */ .chroma .ne { }
|
||||||
color: var(--cyan);
|
/* NameAttribute */ .chroma .na,
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
/* Name */
|
|
||||||
.chroma .n {
|
|
||||||
}
|
|
||||||
/* NameBuiltinPseudo */
|
|
||||||
.chroma .bp {
|
|
||||||
}
|
|
||||||
/* NameConstant */
|
|
||||||
.chroma .no {
|
|
||||||
}
|
|
||||||
/* NameDecorator */
|
|
||||||
.chroma .nd {
|
|
||||||
}
|
|
||||||
/* NameEntity */
|
|
||||||
.chroma .ni {
|
|
||||||
}
|
|
||||||
/* NameException */
|
|
||||||
.chroma .ne {
|
|
||||||
}
|
|
||||||
/* NameAttribute */
|
|
||||||
.chroma .na,
|
|
||||||
/* NameClass */ .chroma .nc,
|
/* NameClass */ .chroma .nc,
|
||||||
/* NameFunctionMagic */ .chroma .fm,
|
/* NameFunctionMagic */ .chroma .fm,
|
||||||
/* NameFunction */ .chroma .nf {
|
/* NameFunction */ .chroma .nf { color:var(--green) }
|
||||||
color: var(--green);
|
/* NameNamespace */ .chroma .nn { }
|
||||||
}
|
/* NameOther */ .chroma .nx { }
|
||||||
/* NameNamespace */
|
/* NameProperty */ .chroma .py { }
|
||||||
.chroma .nn {
|
/* NameTag */ .chroma .nt { color:var(--pink) }
|
||||||
}
|
/* NameBuiltin */ .chroma .nb,
|
||||||
/* NameOther */
|
|
||||||
.chroma .nx {
|
|
||||||
}
|
|
||||||
/* NameProperty */
|
|
||||||
.chroma .py {
|
|
||||||
}
|
|
||||||
/* NameTag */
|
|
||||||
.chroma .nt {
|
|
||||||
color: var(--pink);
|
|
||||||
}
|
|
||||||
/* NameBuiltin */
|
|
||||||
.chroma .nb,
|
|
||||||
/* NameLabel */ .chroma .nl,
|
/* NameLabel */ .chroma .nl,
|
||||||
/* NameVariable */ .chroma .nv,
|
/* NameVariable */ .chroma .nv,
|
||||||
/* NameVariableClass */ .chroma .vc,
|
/* NameVariableClass */ .chroma .vc,
|
||||||
/* NameVariableGlobal */ .chroma .vg,
|
/* NameVariableGlobal */ .chroma .vg,
|
||||||
/* NameVariableInstance */ .chroma .vi {
|
/* NameVariableInstance */ .chroma .vi { color:var(--cyan);font-style:italic }
|
||||||
color: var(--cyan);
|
/* NameVariableMagic */ .chroma .vm { }
|
||||||
font-style: italic;
|
/* Literal */ .chroma .l { }
|
||||||
}
|
/* LiteralDate */ .chroma .ld { }
|
||||||
/* NameVariableMagic */
|
/* LiteralString */ .chroma .s,
|
||||||
.chroma .vm {
|
|
||||||
}
|
|
||||||
/* Literal */
|
|
||||||
.chroma .l {
|
|
||||||
}
|
|
||||||
/* LiteralDate */
|
|
||||||
.chroma .ld {
|
|
||||||
}
|
|
||||||
/* LiteralString */
|
|
||||||
.chroma .s,
|
|
||||||
/* LiteralStringAffix */ .chroma .sa,
|
/* LiteralStringAffix */ .chroma .sa,
|
||||||
/* LiteralStringBacktick */ .chroma .sb,
|
/* LiteralStringBacktick */ .chroma .sb,
|
||||||
/* LiteralStringChar */ .chroma .sc,
|
/* LiteralStringChar */ .chroma .sc,
|
||||||
|
@ -162,85 +77,34 @@
|
||||||
/* LiteralStringOther */ .chroma .sx,
|
/* LiteralStringOther */ .chroma .sx,
|
||||||
/* LiteralStringRegex */ .chroma .sr,
|
/* LiteralStringRegex */ .chroma .sr,
|
||||||
/* LiteralStringSingle */ .chroma .s1,
|
/* LiteralStringSingle */ .chroma .s1,
|
||||||
/* LiteralStringSymbol */ .chroma .ss {
|
/* LiteralStringSymbol */ .chroma .ss { color:var(--yellow) }
|
||||||
color: var(--yellow);
|
/* LiteralNumber */ .chroma .m,
|
||||||
}
|
|
||||||
/* LiteralNumber */
|
|
||||||
.chroma .m,
|
|
||||||
/* LiteralNumberBin */ .chroma .mb,
|
/* LiteralNumberBin */ .chroma .mb,
|
||||||
/* LiteralNumberFloat */ .chroma .mf,
|
/* LiteralNumberFloat */ .chroma .mf,
|
||||||
/* LiteralNumberHex */ .chroma .mh,
|
/* LiteralNumberHex */ .chroma .mh,
|
||||||
/* LiteralNumberInteger */ .chroma .mi,
|
/* LiteralNumberInteger */ .chroma .mi,
|
||||||
/* LiteralNumberIntegerLong */ .chroma .il,
|
/* LiteralNumberIntegerLong */ .chroma .il,
|
||||||
/* LiteralNumberOct */ .chroma .mo {
|
/* LiteralNumberOct */ .chroma .mo { color:var(--purple) }
|
||||||
color: var(--purple);
|
/* Operator */ .chroma .o,
|
||||||
}
|
/* OperatorWord */ .chroma .ow { color:var(--pink) }
|
||||||
/* Operator */
|
/* Punctuation */ .chroma .p { }
|
||||||
.chroma .o,
|
/* Comment */ .chroma .c,
|
||||||
/* OperatorWord */ .chroma .ow {
|
|
||||||
color: var(--pink);
|
|
||||||
}
|
|
||||||
/* Punctuation */
|
|
||||||
.chroma .p {
|
|
||||||
}
|
|
||||||
/* Comment */
|
|
||||||
.chroma .c,
|
|
||||||
/* CommentHashbang */ .chroma .ch,
|
/* CommentHashbang */ .chroma .ch,
|
||||||
/* CommentMultiline */ .chroma .cm,
|
/* CommentMultiline */ .chroma .cm,
|
||||||
/* CommentSingle */ .chroma .c1,
|
/* CommentSingle */ .chroma .c1,
|
||||||
/* CommentSpecial */ .chroma .cs {
|
/* CommentSpecial */ .chroma .cs { color:var(--comment) }
|
||||||
color: var(--comment);
|
/* CommentPreproc */ .chroma .cp,
|
||||||
}
|
/* CommentPreprocFile */ .chroma .cpf { color:var(--pink) }
|
||||||
/* CommentPreproc */
|
/* Generic */ .chroma .g { }
|
||||||
.chroma .cp,
|
/* GenericDeleted */ .chroma .gd { color:var(--red) }
|
||||||
/* CommentPreprocFile */ .chroma .cpf {
|
/* GenericEmph */ .chroma .ge { text-decoration:underline }
|
||||||
color: var(--pink);
|
/* GenericError */ .chroma .gr { }
|
||||||
}
|
/* GenericHeading */ .chroma .gh { font-weight:bold }
|
||||||
/* Generic */
|
/* GenericInserted */ .chroma .gi { color:var(--green);font-weight:bold }
|
||||||
.chroma .g {
|
/* GenericOutput */ .chroma .go { color:var(--comment) }
|
||||||
}
|
/* GenericPrompt */ .chroma .gp { }
|
||||||
/* GenericDeleted */
|
/* GenericStrong */ .chroma .gs { }
|
||||||
.chroma .gd {
|
/* GenericSubheading */ .chroma .gu { font-weight:bold }
|
||||||
color: var(--red);
|
/* GenericTraceback */ .chroma .gt { }
|
||||||
}
|
/* GenericUnderline */ .chroma .gl { text-decoration:underline }
|
||||||
/* GenericEmph */
|
/* TextWhitespace */ .chroma .w { }
|
||||||
.chroma .ge {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
/* GenericError */
|
|
||||||
.chroma .gr {
|
|
||||||
}
|
|
||||||
/* GenericHeading */
|
|
||||||
.chroma .gh {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
/* GenericInserted */
|
|
||||||
.chroma .gi {
|
|
||||||
color: var(--green);
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
/* GenericOutput */
|
|
||||||
.chroma .go {
|
|
||||||
color: var(--comment);
|
|
||||||
}
|
|
||||||
/* GenericPrompt */
|
|
||||||
.chroma .gp {
|
|
||||||
color: var(--comment);
|
|
||||||
}
|
|
||||||
/* GenericStrong */
|
|
||||||
.chroma .gs {
|
|
||||||
}
|
|
||||||
/* GenericSubheading */
|
|
||||||
.chroma .gu {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
/* GenericTraceback */
|
|
||||||
.chroma .gt {
|
|
||||||
}
|
|
||||||
/* GenericUnderline */
|
|
||||||
.chroma .gl {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
/* TextWhitespace */
|
|
||||||
.chroma .w {
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,295 +0,0 @@
|
||||||
+++
|
|
||||||
title = "Zosimos Part 1: Workflow Setup"
|
|
||||||
date = 2025-03-31T12:00:00-04:00
|
|
||||||
tags = ["osdev", "zig", "raspberry pi"]
|
|
||||||
+++
|
|
||||||
|
|
||||||
Programming my own OS has been a dream of mine for years, but my attempts always
|
|
||||||
end up going the same way: writing some code that can run in a virtual machine,
|
|
||||||
learning some interesting stuff about x86_64, and then giving up before I even
|
|
||||||
reach userspace. Now I find myself called again by the siren song, but I'm
|
|
||||||
determined to not go down the same road again ... so I'm doing it on an arm
|
|
||||||
machine this time. I ended up settling on the Raspberry Pi 4B, since the price
|
|
||||||
was reasonable and the large community makes it easy to find answers to any
|
|
||||||
question I might have. But before I can set out to build the greatest OS ever,
|
|
||||||
I need to set up a good workflow for building and testing.
|
|
||||||
|
|
||||||
## Building the Kernel
|
|
||||||
|
|
||||||
For this go around, I decided to write my kernel in Zig. I've gotten too used to
|
|
||||||
the conveniences of a modern language to go back to C, and while I love Rust
|
|
||||||
dearly I've found myself frustrated by having to use shell scripts and make to
|
|
||||||
take care of the parts of getting to a bootable image that Cargo doesn't concern
|
|
||||||
itself with. Zig's build system is flexible enough to handle the whole process
|
|
||||||
itself, and while nothing in this post is particularly difficult, I have reason
|
|
||||||
to believe it'll keep up even as the complexity increases.
|
|
||||||
|
|
||||||
I won't go into detail about the actual code since 1\. it's extremely trivial
|
|
||||||
and 2\. it's not the main focus of this post. All that matters is that it
|
|
||||||
consists of some Zig code, an assembly stub, and a linker script to put it all
|
|
||||||
together. In our `build.zig` file we therefore write:
|
|
||||||
|
|
||||||
```zig
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub fn build(b: *std.Build) void {
|
|
||||||
const target = b.standardTargetOption(.{});
|
|
||||||
const optimize = b.standardOptimizeOption(.{});
|
|
||||||
|
|
||||||
const kernel = b.addExecutable(.{
|
|
||||||
.name = "kernel",
|
|
||||||
.root_source_file = b.path("src/main.zig"),
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
});
|
|
||||||
kernel.setLinkerScript(b.path("src/Link.ld"));
|
|
||||||
kernel.addAssemblyFile(b.path("src/startup.s"));
|
|
||||||
|
|
||||||
b.installArtifact(kernel);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
As it is, this will try to build our kernel for the host machine and OS.
|
|
||||||
To get a freestanding binary we need to change `target`:
|
|
||||||
|
|
||||||
```zig
|
|
||||||
const target = b.resolveTargetQuery(.{
|
|
||||||
.cpu_arch = .aarch64,
|
|
||||||
.os_tag = .freestanding,
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
Now if we run `zig build` we'll see our compiled kernel in `zig-out/bin/kernel`.
|
|
||||||
But we're not done yet. This is an ELF file, and the Pi only knows how to boot
|
|
||||||
flat binaries. We'll still keep the ELF around for debugging, but we add the
|
|
||||||
following to create a flat binary:[^2]
|
|
||||||
|
|
||||||
```zig
|
|
||||||
const kernel_bin = b.addObjCopy(kernel.getEmittedBin(), .{
|
|
||||||
.format = .bin,
|
|
||||||
.basename = "kernel.img",
|
|
||||||
});
|
|
||||||
const install_kernel = b.addInstallBinFile(
|
|
||||||
kernel_bin.getOutput(),
|
|
||||||
"kernel8.img",
|
|
||||||
);
|
|
||||||
b.getInstallStep().dependOn(&install_kernel.step);
|
|
||||||
```
|
|
||||||
|
|
||||||
Before we can boot this kernel image though, we need some other stuff. The Pi is
|
|
||||||
a little strange in that it reads most of its firmware from the boot drive
|
|
||||||
instead of from flash on the board. Normally we'd just download these firmware
|
|
||||||
files ourselves, but we can do better!
|
|
||||||
|
|
||||||
[^2]: The filename `kernel8.img` is important since it tells the board to boot
|
|
||||||
up in 64 bit mode. This can be overridden in [`config.txt`][7] if you really
|
|
||||||
want a different filename.
|
|
||||||
|
|
||||||
[1]: https://git.wires.systems/wires/zosimos/src/commit/8466e9b4d2fbca85d53d8dadc87914b4766c43de/src
|
|
||||||
[7]: https://www.raspberrypi.com/documentation/computers/config_txt.html
|
|
||||||
|
|
||||||
## Fetching Firmware
|
|
||||||
|
|
||||||
A great thing about the Zig package manager is that it can be used to fetch any
|
|
||||||
dependencies, not just Zig packages. In this case we're going to use it to grab
|
|
||||||
the firmware files for the Pi so that we can reference it in our build script.
|
|
||||||
If we run:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ zig fetch --save=rpi_firmware 'https://github.com/raspberrypi/firmware/archive/refs/tags/1.20250305.tar.gz'
|
|
||||||
```
|
|
||||||
|
|
||||||
then the latest (at time of writing) release of the firmware will be added to
|
|
||||||
our project as a dependency. This also provides us a way to ensure the integrity
|
|
||||||
of the firmware (at least that it hasn't changed since we first fetched it)
|
|
||||||
since it's saved alongside a hash. Let's adjust the build script to place all
|
|
||||||
the files we need[^3] in a `boot` directory:
|
|
||||||
|
|
||||||
[^3]: I determined this by trial and error, but it's probably documented
|
|
||||||
somewhere I missed.
|
|
||||||
|
|
||||||
```zig
|
|
||||||
const kernel_bin = b.addObjCopy(kernel.getEmittedBin(), .{
|
|
||||||
.format = .bin,
|
|
||||||
.basename = "kernel.img",
|
|
||||||
});
|
|
||||||
const install_kernel = b.addInstallFile(
|
|
||||||
kernel_bin.getOutput(),
|
|
||||||
"boot/kernel8.img",
|
|
||||||
);
|
|
||||||
b.getInstallStep().dependOn(&install_kernel.step);
|
|
||||||
|
|
||||||
const firmware = b.dependency("rpi_firmware", .{});
|
|
||||||
b.installDirectory(.{
|
|
||||||
.source_dir = firmware.path("boot"),
|
|
||||||
.install_dir = .prefix,
|
|
||||||
.install_subdir = "boot",
|
|
||||||
.include_extensions = &.{
|
|
||||||
"start4.elf",
|
|
||||||
"start4db.elf",
|
|
||||||
"fixup4.dat",
|
|
||||||
"bcm2711-rpi-4-b.dtb",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## Network Booting
|
|
||||||
|
|
||||||
Having to repeatedly remove the memory card, transfer a new kernel to it, and
|
|
||||||
put it back in sounds like a massive pain. Luckily, the Pi4B supports network
|
|
||||||
booting from a tftp server. The simplest way to set this up would be to install
|
|
||||||
the `tftp-hpa` package and then just run the server normally, having it serve
|
|
||||||
the `zig-out/boot` folder. But I don't like having daemons that I'm only going
|
|
||||||
to use for a single project installed on my system, so for no real reason other
|
|
||||||
than aesthetics I'm going to be running the tftp server inside a container. The
|
|
||||||
Dockerfile is reproduced below in its entirety:
|
|
||||||
|
|
||||||
```dockerfile
|
|
||||||
# docker/tftp/Dockerfile
|
|
||||||
|
|
||||||
FROM alpine:3
|
|
||||||
|
|
||||||
RUN apk add --no-cache tftp-hpa
|
|
||||||
|
|
||||||
ENTRYPOINT ["in.tftpd"]
|
|
||||||
```
|
|
||||||
|
|
||||||
This is combined with the following `compose.yaml`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
services:
|
|
||||||
tftp:
|
|
||||||
build: ./docker/tftp
|
|
||||||
ports:
|
|
||||||
- 69:69/udp
|
|
||||||
volumes:
|
|
||||||
- ./zig-out/boot:/data:ro
|
|
||||||
command: --verbose --foreground --secure /data
|
|
||||||
```
|
|
||||||
|
|
||||||
running `docker compose up` should now start the tftp server properly.[^1]
|
|
||||||
|
|
||||||
[^1]: Since tftpd only logs to syslog and the container won't normally have
|
|
||||||
a syslogd running in it, you'll have to modify the setup if you want to see
|
|
||||||
logs. I initially did this during testing by using busybox's syslogd in the
|
|
||||||
container, but found that it made shutdown times very long, which wasn't
|
|
||||||
ideal when frequently restarting the container.
|
|
||||||
|
|
||||||
Lastly, we have to configure the Pi itself. Network boot config info is stored
|
|
||||||
in the board's EEPROM, so boot into Linux on the Pi and run
|
|
||||||
|
|
||||||
|
|
||||||
```console
|
|
||||||
# rpi-eeprom-config --edit
|
|
||||||
```
|
|
||||||
|
|
||||||
This will bring up a text editor where you can edit the EEPROM variables in
|
|
||||||
a `.ini`-like format. We'll write the following config:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
[all]
|
|
||||||
BOOT_UART=1
|
|
||||||
BOOT_ORDER=0xf12
|
|
||||||
|
|
||||||
TFTP_PREFIX=1
|
|
||||||
TFTP_IP=x.y.z.w
|
|
||||||
```
|
|
||||||
|
|
||||||
where `TFTP_IP` should be set to the IP of your tftp server. All of these
|
|
||||||
options are documented [here][2] if you want to know what specifically they do.
|
|
||||||
|
|
||||||
[2]: https://www.raspberrypi.com/documentation/computers/raspberry-pi.html
|
|
||||||
|
|
||||||
Now, assuming it has a connection, our Pi should successfully boot over the
|
|
||||||
network! But since all our so-called kernel does at this point is spin forever,
|
|
||||||
how can we tell that anything's even happening?
|
|
||||||
|
|
||||||
## Remote Debugging
|
|
||||||
|
|
||||||
The easiest choice would probably be to make the program actually do something,
|
|
||||||
like blink the activity LED, but I've decided to set up remote debugging
|
|
||||||
instead. I'm using the [FT2232H mini module][3] for this, since it lets me do
|
|
||||||
both UART and JTAG over a single USB connection on my dev machine. For an
|
|
||||||
explanation of how to hook it up to the Pi, see [this blog post][4]. Like the
|
|
||||||
author of that post I'll be using OpenOCD in a docker container to connect to
|
|
||||||
the board, but I went about it a little differently. Since 2021, OpenOCD has
|
|
||||||
included a config for the Pi4B by default, so I only had to write the following
|
|
||||||
short config for the FT2232H:
|
|
||||||
|
|
||||||
```tcl
|
|
||||||
# docker/openocd/interface/ft2232h.cfg
|
|
||||||
|
|
||||||
adapter driver ftdi
|
|
||||||
|
|
||||||
ftdi device_desc "FT2232H MiniModule"
|
|
||||||
ftdi vid_pid 0x0403 0x6010
|
|
||||||
|
|
||||||
ftdi layout_init 0x0000 0x000b
|
|
||||||
|
|
||||||
ftdi channel 0
|
|
||||||
```
|
|
||||||
|
|
||||||
I'm extremely new to OpenOCD and JTAG in general, so for all I know this might
|
|
||||||
be terribly broken in some way I just haven't noticed yet, but it's been working
|
|
||||||
so far. The Dockerfile is then as follows:
|
|
||||||
|
|
||||||
```dockerfile
|
|
||||||
# docker/openocd/Dockerfile
|
|
||||||
|
|
||||||
FROM alpine:3
|
|
||||||
|
|
||||||
RUN apk add --no-cache openocd
|
|
||||||
|
|
||||||
ADD interface/ft2232h.cfg /usr/share/openocd/scripts/interface/
|
|
||||||
|
|
||||||
ENTRYPOINT ["openocd"]
|
|
||||||
```
|
|
||||||
|
|
||||||
And we add the following entry under `services` in `compose.yaml`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
openocd:
|
|
||||||
build: ./docker/openocd
|
|
||||||
ports:
|
|
||||||
- 6666:6666
|
|
||||||
- 4444:4444
|
|
||||||
- 3333:3333
|
|
||||||
devices:
|
|
||||||
- "/dev/bus/usb:/dev/bus/usb"
|
|
||||||
command: -f interface/ft2232h.cfg -f board/rpi4b.cfg -c "bindto 0.0.0.0"
|
|
||||||
```
|
|
||||||
|
|
||||||
Lastly, we need to add a file called `config.txt` with the following contents:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
[all]
|
|
||||||
gpio=22-27=np
|
|
||||||
enable_jtag_gpio=1
|
|
||||||
enable_uart=1
|
|
||||||
uart_2ndstage=1
|
|
||||||
```
|
|
||||||
|
|
||||||
and make sure to install it in our `build.zig`:
|
|
||||||
|
|
||||||
```zig
|
|
||||||
b.installFile("config.txt", "boot/config.txt");
|
|
||||||
```
|
|
||||||
|
|
||||||
Now we should be able to attach to `/dev/ttyUSB0` to see the UART messages when
|
|
||||||
we reboot and connect to OpenOCD's GDB server to debug the running kernel.
|
|
||||||
|
|
||||||
[3]: https://ftdichip.com/products/ft2232h-mini-module/
|
|
||||||
[4]: https://vinnie.work/blog/2021-04-02-ft2232h-rpi4#wiring-up-the-hardware
|
|
||||||
|
|
||||||
## What's Next?
|
|
||||||
|
|
||||||
For the project in general, my next goal is to set up UART communication, and
|
|
||||||
then hopefully some physical memory management with ideas from [this paper][5].
|
|
||||||
As far as the specific subject of this post goes, I want to work on making the
|
|
||||||
startup experience nicer. As it stands right now, OpenOCD is being started at
|
|
||||||
the same time as the tftp server, when the board is never ready, so the
|
|
||||||
container always has to be restarted. It would be good to have it wait until
|
|
||||||
after the board is booted to start trying to connect, but I don't have any great
|
|
||||||
ideas on how to pull that off right now.
|
|
||||||
|
|
||||||
[5]: https://www.usenix.org/system/files/atc23-wrenger.pdf
|
|
|
@ -1,6 +1,6 @@
|
||||||
baseURL = 'https://wires.systems'
|
baseURL = 'https://wires.systems'
|
||||||
languageCode = 'en-us'
|
languageCode = 'en-us'
|
||||||
title = "wires"
|
title = "~wires"
|
||||||
uglyurls = true
|
uglyurls = true
|
||||||
enableGitInfo = true
|
enableGitInfo = true
|
||||||
disableKinds = ['section', 'taxonomy']
|
disableKinds = ['section', 'taxonomy']
|
||||||
|
@ -17,11 +17,6 @@ summaryLength = 10
|
||||||
[outputFormats.rss]
|
[outputFormats.rss]
|
||||||
baseName = 'feed'
|
baseName = 'feed'
|
||||||
|
|
||||||
[[menu.main]]
|
|
||||||
weight = -10
|
|
||||||
name = "home"
|
|
||||||
url = "/"
|
|
||||||
|
|
||||||
[[menu.main]]
|
[[menu.main]]
|
||||||
name = "rss"
|
name = "rss"
|
||||||
url = "/feed.xml"
|
url = "/feed.xml"
|
||||||
|
|
1
layouts/_default/_markup/render-codeblock.html
Normal file
1
layouts/_default/_markup/render-codeblock.html
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<pre class="chroma"><code class="language-{{ .Type }}" data-lang="{{ .Type }}">{{ (transform.HighlightCodeBlock .).Inner }}</code></pre>
|
|
@ -1,3 +0,0 @@
|
||||||
<h{{ .Level }} id="{{ .Anchor }}" {{- with .Attributes.class }} class="{{ . }}" {{- end }}>
|
|
||||||
<a href="#{{ .Anchor }}">{{ .Text }}</a>
|
|
||||||
</h{{ .Level }}>
|
|
|
@ -1,30 +1,29 @@
|
||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html lang="{{ .Site.Language.LanguageCode }}">
|
<html lang="{{ .Site.Language.LanguageCode }}">
|
||||||
<head>
|
<head>
|
||||||
{{ partial "head.html" . }}
|
{{ partial "head.html" . }}
|
||||||
<!-- this is here to make sure that if we can't load CSS the icons don't totally fuck up layout -->
|
<!-- this is here to make sure that if we can't load CSS the icons don't totally fuck up layout -->
|
||||||
<style>
|
<style>.icon svg { width: 1.25em; height: 1.25em; }</style>
|
||||||
.icon svg {
|
{{ range .AlternativeOutputFormats -}}
|
||||||
width: 1.25em;
|
{{ printf `<link rel="%s" type="%s" href="%s" title="%s" />` .Rel .MediaType.Type .Permalink $.Site.Title | safeHTML }}
|
||||||
height: 1.25em;
|
{{ end -}}
|
||||||
}
|
</head>
|
||||||
</style>
|
<body>
|
||||||
{{ range .AlternativeOutputFormats -}} {{ printf `
|
<a href="#main" class="skip">Skip to content</a>
|
||||||
<link rel="%s" type="%s" href="%s" title="%s" />
|
<nav id="main-nav">
|
||||||
` .Rel .MediaType.Type .Permalink $.Site.Title | safeHTML }} {{ end -}}
|
<ul>
|
||||||
</head>
|
<li><a href="{{.Site.BaseURL}}" id="site-title">{{.Site.Title}}</a></li>
|
||||||
<body>
|
{{ with .Site.Menus.main }}
|
||||||
<a href="#main" class="skip">Skip to content</a>
|
{{ range . }}
|
||||||
<nav id="main-nav">
|
|
||||||
<div id="site-title">{{.Site.Title}}</div>
|
|
||||||
<ul>
|
|
||||||
{{ with .Site.Menus.main }} {{ range . }}
|
|
||||||
<li><a href="{{ .URL }}">{{ .Name }}</a></li>
|
<li><a href="{{ .URL }}">{{ .Name }}</a></li>
|
||||||
{{ end }} {{ end }}
|
{{ end }}
|
||||||
</ul>
|
{{ end }}
|
||||||
</nav>
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
<main id="main">{{ block "main" . }}{{ end }}</main>
|
<main id="main">
|
||||||
{{ partial "footer.html" . }}
|
{{ block "main" . }}{{ end }}
|
||||||
</body>
|
</main>
|
||||||
|
{{ partial "footer.html" . }}
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -6,8 +6,16 @@
|
||||||
{{ with .Keywords }}<meta name="keywords" content="{{ delimit . ", " }}"/>{{ end }}
|
{{ with .Keywords }}<meta name="keywords" content="{{ delimit . ", " }}"/>{{ end }}
|
||||||
<link rel="license" href="https://creativecommons.org/licenses/by-sa/4.0/"/>
|
<link rel="license" href="https://creativecommons.org/licenses/by-sa/4.0/"/>
|
||||||
<link rel="canonical" href="{{ .Permalink }}"/>
|
<link rel="canonical" href="{{ .Permalink }}"/>
|
||||||
{{ range slice "style.css" "syntax.css"}}
|
{{- with resources.Get "style.css" }}
|
||||||
{{- with resources.Get . }}
|
{{- if eq hugo.Environment "development" }}
|
||||||
|
<link rel="stylesheet" href="{{ .RelPermalink }}">
|
||||||
|
{{- else }}
|
||||||
|
{{- with . | minify | fingerprint }}
|
||||||
|
<link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with resources.Get "syntax.css" }}
|
||||||
{{- if eq hugo.Environment "development" }}
|
{{- if eq hugo.Environment "development" }}
|
||||||
<link rel="stylesheet" href="{{ .RelPermalink }}">
|
<link rel="stylesheet" href="{{ .RelPermalink }}">
|
||||||
{{- else }}
|
{{- else }}
|
||||||
|
@ -16,6 +24,5 @@
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{ end }}
|
|
||||||
<title>{{ if .IsHome }}{{ site.Title }}{{ else }}{{ printf "%s | %s" .Title site.Title }}{{ end }}</title>
|
<title>{{ if .IsHome }}{{ site.Title }}{{ else }}{{ printf "%s | %s" .Title site.Title }}{{ end }}</title>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
{{ with .GetTerms "tags" }}
|
{{ with .GetTerms "tags" }}
|
||||||
<p>
|
<br>
|
||||||
Tags:{{ range $i, $e := . }}{{- if $i -}},{{ end -}}{{ with $e }}
|
tags:
|
||||||
<a class="tag" href="{{.Permalink}}">{{lower .Title}}</a>{{end}}{{ end }}
|
{{ range $i, $e := . }}{{- if $i -}}, {{ end -}}{{ with $e }}<a class="tag" href="{{.Permalink}}">{{lower .Title}}</a>{{end}}{{ end }}
|
||||||
</p>
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
Loading…
Add table
Reference in a new issue