Skip to content
Permalink
Browse files

Permanent move to Zig

I've decided to continue development with Zig. Rust source code is now
removed from master, and can be found by going back through commits if
so desired. See here for reasoning:
https://sjdh.us/blog/continuing-with-zig.html
  • Loading branch information...
sjdh02 committed Feb 11, 2019
2 parents d65f2ae + 2315b40 commit 39cda4b516a8fe3d0c3c593316165cb938f8ffa5
@@ -0,0 +1 @@
zig-cache/
@@ -1,41 +1,76 @@
# trOS
trOS is a small, rust and assembly, aarch64 rpi3 bare metal OS thingy.
trOS is a small, [zig](https://ziglang.org) and assembly, aarch64 RPI3 bare metal OS thingy.

Here is some stuff that works:
some stuff that works:
* mailbox calls
* uart0
* framebuffer (initializing/clearing/printing characters)
* framebuffer (initializing/clearing/printing characters and strings)
* gpio
* mmio

Here is some stuff that doesn't quite work:
stuff that is being worked on:
* USB
* networking
* reading from the sd card
* anything not mentioned in the "stuff that works" section
* SD card support (priority!)
* anything else not mentioned above

# Building
You'll need `cargo` and `cargo-xbuild` for the build. once you have those,
run:
# building
all you need to build is [zig](https://ziglang.org) itself. grab it and run:

```
zig build
```

the output file will be in `zig-cache`. alternatively, you can run the following to
launch qemu (will auto-detect windows or unix):

```
zig build qemu
```

you can have qemu redirect to a pty as well:

```
zig build qemu -Dpty
```

you can start a gdb remote server:

```
zig build qemu -Dgdb
```
sh build

you can combine the last two:

```
zig build qemu -Dpty -Dgdb
```

if you want a very small binary:

```
zig build -Drelease-fast
# or
./build
zig build -Drelease-small
```
the binary will be in `trOS_core/target/aarch64-unknown-none/debug/`.

# Running
You can run this with qemu or use objcopy to throw it in an img file for booting
on real hardware. Note that it hasn't been tested on real hardware, but should work
fine. Running with qemu is easy:
both of these produce a binary that is about ~5kb.

if you want release optimizations while still having safety checks:

```
qemu-system-aarch64 -kernel trOS_core/target/aarch64-unknown-none/debug/trOS -m 256 -M raspi3 -serial stdio
zig build -Drelease-safe
```
You can redirect `-serial` wherever you prefer.

Alternatively, to both build and run with qemu, make the `run` file executable and run it.
It invokes the `xbuild` command and then fires up qemu.
and thats about all the build options. note that you can combine all `-D` options
with the `qemu` directive, e.g.:

```
zig build qemu -Drelease-small
```

will build a `release-small` binary and then run it with qemu.

# credit

# Disclaimer
A lot of this code is far from safe and likely not too idiomatic. Don't expect anything amazing here,
at least not for now. Just know that I do intend to improve it over time.
thanks to [andrew kelly](https://github.com/andrewrk/clashos/) for the build file.
3 build

This file was deleted.

Oops, something went wrong.
@@ -0,0 +1,45 @@
const builtin = @import("builtin");
const std = @import("std");
const Builder = std.build.Builder;

pub fn build(b: *Builder) !void {
const want_gdb = b.option(bool, "gdb", "Build for QEMU gdb server") orelse false;
const want_pty = b.option(bool, "pty", "Create a separate serial port path") orelse false;

const mode = b.standardReleaseOptions();
const exe = b.addStaticExecutable("tOS", "src/kernel.zig");
exe.addAssemblyFile("src/asm/boot.S");
exe.addIncludeDir("src/vga");
exe.setBuildMode(mode);

exe.setLinkerScriptPath("./linker.ld");
// Use eabihf for freestanding arm code with hardware float support
exe.setTarget(builtin.Arch.aarch64v8, builtin.Os.freestanding, builtin.Environ.eabihf);

const qemu = b.step("qemu", "run kernel in qemu");
var qemu_args = std.ArrayList([]const u8).init(b.allocator);
try qemu_args.appendSlice([][]const u8{
if (builtin.os == builtin.Os.windows) "C:/Program Files/qemu/qemu-system-aarch64.exe" else "qemu-system-aarch64",
"-kernel",
exe.getOutputPath(),
"-m",
"256",
"-M",
"raspi3",
"-serial",
if (want_pty) "pty" else "stdio",
});
if (want_gdb) {
try qemu_args.appendSlice([][]const u8{
"-S",
"-s",
});
}
const run_qemu = b.addCommand(null, b.env_map, qemu_args.toSliceConst());
qemu.dependOn(&run_qemu.step);
run_qemu.step.dependOn(&exe.step);


b.default_step.dependOn(&exe.step);
b.installArtifact(exe);
}
File renamed without changes.
3 run

This file was deleted.

Oops, something went wrong.
File renamed without changes.
@@ -0,0 +1,11 @@
pub const FrameBufferError = error {
InitializationError,
};

pub const CommandError = error {
ParseError,
};

pub const RegisterError = error {
BadType,
};
@@ -0,0 +1,9 @@
pub const mmio = @import("io/mmio.zig");
pub const gpio = @import("io/gpio.zig");
pub const mbox = @import("mbox.zig");
pub const uart = @import("io/uart.zig");
pub const framebuffer = @import("vga/framebuffer.zig");
pub const regs = @import("regs.zig");

pub const errorTypes = @import("errors.zig");
pub const util = @import("util.zig");
@@ -0,0 +1,24 @@
const index = @import("../index.zig");

const mmio = index.mmio;

const Register = index.regs.Register;

pub const GPFSEL0: u32 = mmio.MMIO_BASE + 0x00200000;
pub const GPFSEL1: Register = Register { .ReadWrite = mmio.MMIO_BASE + 0x00200004 };
pub const GPFSEL2: u32 = mmio.MMIO_BASE + 0x00200008;
pub const GPFSEL3: u32 = mmio.MMIO_BASE + 0x0020000C;
pub const GPFSEL4: u32 = mmio.MMIO_BASE + 0x00200010;
pub const GPFSEL5: u32 = mmio.MMIO_BASE + 0x00200014;
pub const GPSET0: u32 = mmio.MMIO_BASE + 0x0020001C;
pub const GPSET1: u32 = mmio.MMIO_BASE + 0x00200020;
pub const GPCLR0: u32 = mmio.MMIO_BASE + 0x00200028;
pub const GPLEV0: u32 = mmio.MMIO_BASE + 0x00200034;
pub const GPLEV1: u32 = mmio.MMIO_BASE + 0x00200038;
pub const GPEDS0: u32 = mmio.MMIO_BASE + 0x00200040;
pub const GPEDS1: u32 = mmio.MMIO_BASE + 0x00200044;
pub const GPHEN0: u32 = mmio.MMIO_BASE + 0x00200064;
pub const GPHEN1: u32 = mmio.MMIO_BASE + 0x00200068;
pub const GPPUD: Register = Register { .WriteOnly = mmio.MMIO_BASE + 0x00200094 };
pub const GPPUDCLK0: Register = Register { .WriteOnly = mmio.MMIO_BASE + 0x00200098 };
pub const GPPUDCLK1: u32 = mmio.MMIO_BASE + 0x0020009C;
@@ -0,0 +1,78 @@
const AtomicOrder = @import("builtin").AtomicOrder;
const index = @import("../index.zig");
const regs = index.regs;
const errorTypes = index.errorTypes;

const Register = regs.Register;

/// Base address for the MMIO operations.
pub const MMIO_BASE: u32 = 0x3F000000;

/// Write data to a given MMIO register, but by default assume that the write
/// is going to be safe and will hit unreachable code if a bad register type
/// is caught by `write`.
pub fn writeSafe(reg: Register, data: u32) void {
write(reg, data) catch unreachable;
}

/// Write data to a given MMIO register, returning an errorTypes.Register.BadType
/// if the register had an incorrect type. Only use this function if, for whatever
/// reason, you want custom error handling of a bad register type being passed.
pub fn write(reg: Register, data: u32) !void {
@fence(AtomicOrder.SeqCst);
switch (reg) {
Register.ReadOnly => {
return errorTypes.RegisterError.BadType;
},
Register.WriteOnly => {
@intToPtr(*volatile u32, reg.WriteOnly).* = data;
},
Register.ReadWrite => {
@intToPtr(*volatile u32, reg.ReadWrite).* = data;
}
}
}

/// Read data to a given MMIO register, but by default assume that the read
/// is going to be safe and will hit unreachable code if a bad register type
/// is caught by `read`.
pub fn readSafe(reg: Register) u32 {
return read(reg) catch unreachable;
}

/// Read data to a given MMIO register, returning an errorTypes.Register.BadType
/// if the register had an incorrect type. Only use this function if, for whatever
/// reason, you want custom error handling of a bad register type being passed.
pub fn read(reg: Register) !u32 {
@fence(AtomicOrder.SeqCst);
switch (reg) {
Register.WriteOnly => {
return errorTypes.RegisterError.BadType;
},
Register.ReadOnly => {
return @intToPtr(*volatile u32, reg.ReadOnly).*;
},
Register.ReadWrite => {
return @intToPtr(*volatile u32, reg.ReadWrite).*;
}
}
}

/// Stall the CPU for a given number of cycles.
pub fn wait(count: usize) void {
var i: usize = 0;
while (i > count) : (i += 1) {
// Marked as volatile so the optimizer doesn't optimize it away
asm volatile ("mov w0, w0");
}
}

test "MMIO read/write" {
const std = @import("std");
const x = Register{ .WriteOnly = 0xDEADBEEF };
const y = Register{ .ReadOnly = 0xDEADBEEF };
const z = Register{ .ReadWrite = 0xDEADBEEF };

std.testing.expectError(errorTypes.RegisterError.BadType, (write(y, 0xCAFEBABE)));
std.testing.expectError(errorTypes.RegisterError.BadType, (read(x)));
}
Oops, something went wrong.

7 comments on commit 39cda4b

@andrewrk

This comment has been minimized.

Copy link

replied Feb 11, 2019

Exciting! I look forward to helping you with whatever Zig issues you run into - apologies in advance for the bugs and lack of maturity as we work towards 1.0.0.

Is your blog post available? I'm getting a 404.

@sjdh02

This comment has been minimized.

Copy link
Owner Author

replied Feb 12, 2019

@andrewrk Wow, didn't expect to see you here! Thanks for offering to help, I'll be sure to check out the various community spots if something gives me trouble. So far though, I have to say, its been very easy to write in Zig, even if the documentation isn't complete yet. The only thing I've run into is an error in framebuffer.zig where a test causes the compiler emits a TODO message.

The blog post isn't up yet, apologies. I'm working on it but some other things got in the way of finishing it when I wanted to, so it'll probably be later tonight before its done.

@sjdh02

This comment has been minimized.

Copy link
Owner Author

replied Feb 12, 2019

@andrewrk Here's that blog post if you're still interested: https://sjdh.us/2019/02/11/continuing-with-zig.html.

@andrewrk

This comment has been minimized.

Copy link

replied Feb 12, 2019

Loved it! I hope we see this on reddit and/or HN soon :)

By the way, this code would be better with a const rather than a var:

const font = @ptrCast(*const PSFFont, &fontEmbed);

That would more closely correspond to the rust code.

I'm also interested in your pointer arithmetic feedback (if you feel generous with your time), since you have experience with zig in a real world project.

@sjdh02

This comment has been minimized.

Copy link
Owner Author

replied Feb 12, 2019

Oh, thanks for catching that - I'll edit it and a few other things before I post to reddit.

So, regarding pointer arithmetic: I was about to put together an example program and everything, only to realize I had made a mistake in my own code and misunderstood what the problem was, so my complaints are actually not valid now. Originally, I was going to make a case for allowing .* on [*]T, but after thinking about it and looking at the code I had, I realized I didn't even need [*]T, and found a much less complicated solution.

@andrewrk

This comment has been minimized.

Copy link

replied Feb 12, 2019

I'll note that if you have a [*]T and you want to do .* you can instead do [0] which is only 1 more keystroke.

@sjdh02

This comment has been minimized.

Copy link
Owner Author

replied Feb 12, 2019

Ah, I hadn't even thought about that. I'll keep that in mind if I need to work with [*]T in the future.

Please sign in to comment.
You can’t perform that action at this time.