Compare commits
2 commits
a6013bef3e
...
909798eb05
Author | SHA1 | Date | |
---|---|---|---|
909798eb05 | |||
dcee4fe4b1 |
9 changed files with 622 additions and 154 deletions
|
@ -1,3 +1,14 @@
|
|||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--fg: #cdd6f4;
|
||||
--bg: #11111b;
|
||||
--link: #bd93f9;
|
||||
--overlay: #313244;
|
||||
}
|
||||
|
||||
.skip {
|
||||
position: absolute;
|
||||
left: -10000px;
|
||||
|
@ -14,33 +25,47 @@
|
|||
}
|
||||
|
||||
body {
|
||||
max-width: 45rem;
|
||||
padding: 0 2rem;
|
||||
margin: 2rem auto 0;
|
||||
line-height: 1.6;
|
||||
background: #11111b;
|
||||
color: white;
|
||||
max-width: 50rem;
|
||||
padding: 2rem;
|
||||
margin: 0 auto;
|
||||
line-height: 1.8;
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
font-family: serif;
|
||||
}
|
||||
|
||||
h1, h2, h3 {
|
||||
margin-bottom: 0.25em;
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
margin-block-end: 0.25em;
|
||||
line-height: 1.3;
|
||||
}
|
||||
font-family: sans-serif;
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
}
|
||||
a,
|
||||
a:visited {
|
||||
color: var(--fg);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
p + p {
|
||||
text-indent: 2em;
|
||||
a:hover {
|
||||
color: var(--link);
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-top: 0;
|
||||
margin-block-start: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-block-start: 0;
|
||||
text-align: justify;
|
||||
hyphens: auto;
|
||||
}
|
||||
|
||||
#main-nav {
|
||||
font-size: 125%;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
#main-nav ul {
|
||||
|
@ -50,24 +75,14 @@ ul {
|
|||
|
||||
#main-nav li {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#main-nav li::before {
|
||||
content: '/ ';
|
||||
}
|
||||
|
||||
#main-nav li:first-child::before {
|
||||
content: '';
|
||||
margin-inline-end: 0.5em;
|
||||
}
|
||||
|
||||
#site-title {
|
||||
font-family: sans-serif;
|
||||
font-weight: bold;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#site-title:hover {
|
||||
text-decoration: underline;
|
||||
font-size: 150%;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
footer {
|
||||
|
@ -82,6 +97,26 @@ header {
|
|||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
a, a:visited {
|
||||
color: #8ef;
|
||||
a,
|
||||
a:visited {
|
||||
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%;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
--cyan: #8be9fd;
|
||||
--green: #50fa7b;
|
||||
--orange: #ffb86c;
|
||||
--pink: #f5a;
|
||||
--pink: #ff79c6;
|
||||
--purple: #bd93f9;
|
||||
--red: #f55;
|
||||
--yellow: #f1fa8c;
|
||||
|
@ -22,49 +22,134 @@
|
|||
--yellow: #df8e1d;
|
||||
}
|
||||
} */
|
||||
/* Background */ .bg,
|
||||
/* PreWrapper */ .chroma { background-color:var(--chroma-bg); }
|
||||
/* Other */ .chroma .x { }
|
||||
/* Error */ .chroma .err { }
|
||||
/* CodeLine */ .chroma .cl { }
|
||||
/* 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,
|
||||
/* Background */
|
||||
.bg,
|
||||
/* PreWrapper */ .chroma {
|
||||
background-color: var(--chroma-bg);
|
||||
}
|
||||
/* Other */
|
||||
.chroma .x {
|
||||
}
|
||||
/* Error */
|
||||
.chroma .err {
|
||||
}
|
||||
/* CodeLine */
|
||||
.chroma .cl {
|
||||
}
|
||||
/* 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,
|
||||
/* KeywordNamespace */ .chroma .kn,
|
||||
/* KeywordPseudo */ .chroma .kp,
|
||||
/* KeywordReserved */ .chroma .kr { color:var(--pink) }
|
||||
/* KeywordType */ .chroma .kt { color:var(--cyan) }
|
||||
/* KeywordDeclaration */ .chroma .kd { color:var(--cyan);font-style:italic }
|
||||
/* Name */ .chroma .n { }
|
||||
/* NameBuiltinPseudo */ .chroma .bp { }
|
||||
/* NameConstant */ .chroma .no { }
|
||||
/* NameDecorator */ .chroma .nd { }
|
||||
/* NameEntity */ .chroma .ni { }
|
||||
/* NameException */ .chroma .ne { }
|
||||
/* NameAttribute */ .chroma .na,
|
||||
/* KeywordReserved */ .chroma .kr {
|
||||
color: var(--pink);
|
||||
}
|
||||
/* KeywordType */
|
||||
.chroma .kt {
|
||||
color: var(--cyan);
|
||||
}
|
||||
/* KeywordDeclaration */
|
||||
.chroma .kd {
|
||||
color: var(--cyan);
|
||||
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,
|
||||
/* NameFunctionMagic */ .chroma .fm,
|
||||
/* NameFunction */ .chroma .nf { color:var(--green) }
|
||||
/* NameNamespace */ .chroma .nn { }
|
||||
/* NameOther */ .chroma .nx { }
|
||||
/* NameProperty */ .chroma .py { }
|
||||
/* NameTag */ .chroma .nt { color:var(--pink) }
|
||||
/* NameBuiltin */ .chroma .nb,
|
||||
/* NameFunction */ .chroma .nf {
|
||||
color: var(--green);
|
||||
}
|
||||
/* NameNamespace */
|
||||
.chroma .nn {
|
||||
}
|
||||
/* NameOther */
|
||||
.chroma .nx {
|
||||
}
|
||||
/* NameProperty */
|
||||
.chroma .py {
|
||||
}
|
||||
/* NameTag */
|
||||
.chroma .nt {
|
||||
color: var(--pink);
|
||||
}
|
||||
/* NameBuiltin */
|
||||
.chroma .nb,
|
||||
/* NameLabel */ .chroma .nl,
|
||||
/* NameVariable */ .chroma .nv,
|
||||
/* NameVariableClass */ .chroma .vc,
|
||||
/* NameVariableGlobal */ .chroma .vg,
|
||||
/* NameVariableInstance */ .chroma .vi { color:var(--cyan);font-style:italic }
|
||||
/* NameVariableMagic */ .chroma .vm { }
|
||||
/* Literal */ .chroma .l { }
|
||||
/* LiteralDate */ .chroma .ld { }
|
||||
/* LiteralString */ .chroma .s,
|
||||
/* NameVariableInstance */ .chroma .vi {
|
||||
color: var(--cyan);
|
||||
font-style: italic;
|
||||
}
|
||||
/* NameVariableMagic */
|
||||
.chroma .vm {
|
||||
}
|
||||
/* Literal */
|
||||
.chroma .l {
|
||||
}
|
||||
/* LiteralDate */
|
||||
.chroma .ld {
|
||||
}
|
||||
/* LiteralString */
|
||||
.chroma .s,
|
||||
/* LiteralStringAffix */ .chroma .sa,
|
||||
/* LiteralStringBacktick */ .chroma .sb,
|
||||
/* LiteralStringChar */ .chroma .sc,
|
||||
|
@ -77,34 +162,85 @@
|
|||
/* LiteralStringOther */ .chroma .sx,
|
||||
/* LiteralStringRegex */ .chroma .sr,
|
||||
/* LiteralStringSingle */ .chroma .s1,
|
||||
/* LiteralStringSymbol */ .chroma .ss { color:var(--yellow) }
|
||||
/* LiteralNumber */ .chroma .m,
|
||||
/* LiteralStringSymbol */ .chroma .ss {
|
||||
color: var(--yellow);
|
||||
}
|
||||
/* LiteralNumber */
|
||||
.chroma .m,
|
||||
/* LiteralNumberBin */ .chroma .mb,
|
||||
/* LiteralNumberFloat */ .chroma .mf,
|
||||
/* LiteralNumberHex */ .chroma .mh,
|
||||
/* LiteralNumberInteger */ .chroma .mi,
|
||||
/* LiteralNumberIntegerLong */ .chroma .il,
|
||||
/* LiteralNumberOct */ .chroma .mo { color:var(--purple) }
|
||||
/* Operator */ .chroma .o,
|
||||
/* OperatorWord */ .chroma .ow { color:var(--pink) }
|
||||
/* Punctuation */ .chroma .p { }
|
||||
/* Comment */ .chroma .c,
|
||||
/* LiteralNumberOct */ .chroma .mo {
|
||||
color: var(--purple);
|
||||
}
|
||||
/* Operator */
|
||||
.chroma .o,
|
||||
/* OperatorWord */ .chroma .ow {
|
||||
color: var(--pink);
|
||||
}
|
||||
/* Punctuation */
|
||||
.chroma .p {
|
||||
}
|
||||
/* Comment */
|
||||
.chroma .c,
|
||||
/* CommentHashbang */ .chroma .ch,
|
||||
/* CommentMultiline */ .chroma .cm,
|
||||
/* CommentSingle */ .chroma .c1,
|
||||
/* CommentSpecial */ .chroma .cs { color:var(--comment) }
|
||||
/* CommentPreproc */ .chroma .cp,
|
||||
/* CommentPreprocFile */ .chroma .cpf { color:var(--pink) }
|
||||
/* Generic */ .chroma .g { }
|
||||
/* GenericDeleted */ .chroma .gd { color:var(--red) }
|
||||
/* GenericEmph */ .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 { }
|
||||
/* GenericStrong */ .chroma .gs { }
|
||||
/* GenericSubheading */ .chroma .gu { font-weight:bold }
|
||||
/* GenericTraceback */ .chroma .gt { }
|
||||
/* GenericUnderline */ .chroma .gl { text-decoration:underline }
|
||||
/* TextWhitespace */ .chroma .w { }
|
||||
/* CommentSpecial */ .chroma .cs {
|
||||
color: var(--comment);
|
||||
}
|
||||
/* CommentPreproc */
|
||||
.chroma .cp,
|
||||
/* CommentPreprocFile */ .chroma .cpf {
|
||||
color: var(--pink);
|
||||
}
|
||||
/* Generic */
|
||||
.chroma .g {
|
||||
}
|
||||
/* GenericDeleted */
|
||||
.chroma .gd {
|
||||
color: var(--red);
|
||||
}
|
||||
/* GenericEmph */
|
||||
.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 {
|
||||
}
|
||||
|
|
295
content/zosimos/workflow.md
Normal file
295
content/zosimos/workflow.md
Normal file
|
@ -0,0 +1,295 @@
|
|||
+++
|
||||
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'
|
||||
languageCode = 'en-us'
|
||||
title = "~wires"
|
||||
title = "wires"
|
||||
uglyurls = true
|
||||
enableGitInfo = true
|
||||
disableKinds = ['section', 'taxonomy']
|
||||
|
@ -17,6 +17,11 @@ summaryLength = 10
|
|||
[outputFormats.rss]
|
||||
baseName = 'feed'
|
||||
|
||||
[[menu.main]]
|
||||
weight = -10
|
||||
name = "home"
|
||||
url = "/"
|
||||
|
||||
[[menu.main]]
|
||||
name = "rss"
|
||||
url = "/feed.xml"
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
<pre class="chroma"><code class="language-{{ .Type }}" data-lang="{{ .Type }}">{{ (transform.HighlightCodeBlock .).Inner }}</code></pre>
|
3
layouts/_default/_markup/render-heading.html
Normal file
3
layouts/_default/_markup/render-heading.html
Normal file
|
@ -0,0 +1,3 @@
|
|||
<h{{ .Level }} id="{{ .Anchor }}" {{- with .Attributes.class }} class="{{ . }}" {{- end }}>
|
||||
<a href="#{{ .Anchor }}">{{ .Text }}</a>
|
||||
</h{{ .Level }}>
|
|
@ -1,29 +1,30 @@
|
|||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="{{ .Site.Language.LanguageCode }}">
|
||||
<head>
|
||||
{{ partial "head.html" . }}
|
||||
<!-- this is here to make sure that if we can't load CSS the icons don't totally fuck up layout -->
|
||||
<style>.icon svg { width: 1.25em; height: 1.25em; }</style>
|
||||
{{ range .AlternativeOutputFormats -}}
|
||||
{{ printf `<link rel="%s" type="%s" href="%s" title="%s" />` .Rel .MediaType.Type .Permalink $.Site.Title | safeHTML }}
|
||||
{{ end -}}
|
||||
</head>
|
||||
<body>
|
||||
<a href="#main" class="skip">Skip to content</a>
|
||||
<nav id="main-nav">
|
||||
<ul>
|
||||
<li><a href="{{.Site.BaseURL}}" id="site-title">{{.Site.Title}}</a></li>
|
||||
{{ with .Site.Menus.main }}
|
||||
{{ range . }}
|
||||
<style>
|
||||
.icon svg {
|
||||
width: 1.25em;
|
||||
height: 1.25em;
|
||||
}
|
||||
</style>
|
||||
{{ range .AlternativeOutputFormats -}} {{ printf `
|
||||
<link rel="%s" type="%s" href="%s" title="%s" />
|
||||
` .Rel .MediaType.Type .Permalink $.Site.Title | safeHTML }} {{ end -}}
|
||||
</head>
|
||||
<body>
|
||||
<a href="#main" class="skip">Skip to content</a>
|
||||
<nav id="main-nav">
|
||||
<div id="site-title">{{.Site.Title}}</div>
|
||||
<ul>
|
||||
{{ with .Site.Menus.main }} {{ range . }}
|
||||
<li><a href="{{ .URL }}">{{ .Name }}</a></li>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</ul>
|
||||
</nav>
|
||||
{{ end }} {{ end }}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<main id="main">
|
||||
{{ block "main" . }}{{ end }}
|
||||
</main>
|
||||
{{ partial "footer.html" . }}
|
||||
</body>
|
||||
<main id="main">{{ block "main" . }}{{ end }}</main>
|
||||
{{ partial "footer.html" . }}
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -6,16 +6,8 @@
|
|||
{{ with .Keywords }}<meta name="keywords" content="{{ delimit . ", " }}"/>{{ end }}
|
||||
<link rel="license" href="https://creativecommons.org/licenses/by-sa/4.0/"/>
|
||||
<link rel="canonical" href="{{ .Permalink }}"/>
|
||||
{{- with resources.Get "style.css" }}
|
||||
{{- 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" }}
|
||||
{{ range slice "style.css" "syntax.css"}}
|
||||
{{- with resources.Get . }}
|
||||
{{- if eq hugo.Environment "development" }}
|
||||
<link rel="stylesheet" href="{{ .RelPermalink }}">
|
||||
{{- else }}
|
||||
|
@ -24,5 +16,6 @@
|
|||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
<title>{{ if .IsHome }}{{ site.Title }}{{ else }}{{ printf "%s | %s" .Title site.Title }}{{ end }}</title>
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{{ with .GetTerms "tags" }}
|
||||
<br>
|
||||
tags:
|
||||
{{ range $i, $e := . }}{{- if $i -}}, {{ end -}}{{ with $e }}<a class="tag" href="{{.Permalink}}">{{lower .Title}}</a>{{end}}{{ end }}
|
||||
<p>
|
||||
Tags:{{ range $i, $e := . }}{{- if $i -}},{{ end -}}{{ with $e }}
|
||||
<a class="tag" href="{{.Permalink}}">{{lower .Title}}</a>{{end}}{{ end }}
|
||||
</p>
|
||||
{{ end }}
|
||||
|
|
Loading…
Add table
Reference in a new issue