Skip to content

Commit 31b15c6

Browse files
committed
Works on real hardware again!
This commit adds various changes to ClashOS to get the stack traces and bootloader to work again in qemu and on real hardware. The linker scripts are changed to allow the binary to be linked at the address 0x80000. This is the default address at which the raspberry pi will load kernel8.img when no 'config.txt' file is present on the sd card. To allow stack traces to work, the link register had to be zero'ed, because it contains random stuff on start up on real hardware. It work on qemu, because it sets the link register to zero. The code for the framebuffer is currently comment out, because for some reason the loading over serial fails to work when that code is compiled. But loading over serial now works!
1 parent 69b0fd1 commit 31b15c6

18 files changed

+240
-214
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ the `-Dgdb` to both `zig build` commands.
5454
1. Mount an sdcard with a single FAT32 partition.
5555
2. Copy `boot/*` to `/path/to/sdcard/*`.
5656
3. `zig build`
57-
4. Copy `clashos.bin` to `/path/to/sdcard/kernel7.img`.
57+
4. Copy `clashos.bin` to `/path/to/sdcard/kernel8.img`.
5858

5959
For further changes repeat steps 3 and 4.
6060

boot/config.txt

-10
This file was deleted.

build.zig

+16-7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub fn build(b: *Builder) !void {
77
const want_gdb = b.option(bool, "gdb", "Build for using gdb with qemu") orelse false;
88
const want_pty = b.option(bool, "pty", "Create a separate TTY path") orelse false;
99
const want_nodisplay = b.option(bool, "nodisplay", "No display for qemu") orelse false;
10+
const want_stack_traces = b.option(bool, "stacktraces", "Adds debug info to the executable for runtime stack traces") orelse false;
1011

1112
const arch = builtin.Arch{ .aarch64 = builtin.Arch.Arm64.v8 };
1213
const environ = builtin.Abi.eabihf;
@@ -20,24 +21,32 @@ pub fn build(b: *Builder) !void {
2021
bootloader.strip = true;
2122
bootloader.setOutputDir("zig-cache");
2223

24+
const run_objcopy_bootloader = b.addSystemCommand(&[_][]const u8{
25+
"llvm-objcopy", bootloader.getOutputPath(),
26+
"-O", "binary",
27+
"zig-cache/bootloader.bin",
28+
});
29+
run_objcopy_bootloader.step.dependOn(&bootloader.step);
30+
2331
const exec_name = if (want_gdb) "clashos-dbg" else "clashos";
2432
const exe = b.addExecutable(exec_name, "src/main.zig");
2533
exe.setOutputDir("zig-cache");
2634
exe.setBuildMode(mode);
2735
exe.setTarget(arch, builtin.Os.freestanding, environ);
28-
const linker_script = if (want_gdb) "src/qemu-gdb.ld" else "src/linker.ld";
36+
const linker_script = if (want_gdb or !want_stack_traces) "src/qemu-gdb.ld" else "src/linker.ld";
2937
exe.setLinkerScriptPath(linker_script);
30-
exe.addBuildOption([]const u8, "bootloader_exe_path", b.fmt("\"{}\"", bootloader.getOutputPath()));
31-
exe.step.dependOn(&bootloader.step);
38+
exe.addBuildOption([]const u8, "bootloader_exe_path", b.fmt("\"{}\"", .{"zig-cache/bootloader.bin"}));
39+
exe.addBuildOption(bool, "want_stack_traces", want_stack_traces);
40+
exe.step.dependOn(&run_objcopy_bootloader.step);
3241

33-
const run_objcopy = b.addSystemCommand(&[_][]const u8{
42+
const run_objcopy_clashos = b.addSystemCommand(&[_][]const u8{
3443
"llvm-objcopy", exe.getOutputPath(),
3544
"-O", "binary",
3645
"clashos.bin",
3746
});
38-
run_objcopy.step.dependOn(&exe.step);
47+
run_objcopy_clashos.step.dependOn(&exe.step);
3948

40-
b.default_step.dependOn(&run_objcopy.step);
49+
b.default_step.dependOn(&run_objcopy_clashos.step);
4150

4251
const qemu = b.step("qemu", "Run the OS in qemu");
4352
var qemu_args = std.ArrayList([]const u8).init(b.allocator);
@@ -71,6 +80,6 @@ pub fn build(b: *Builder) !void {
7180
}
7281

7382
const upload = b.step("upload", "Send a new kernel image to a running instance. (See -Dtty option)");
74-
upload.dependOn(&run_objcopy.step);
83+
upload.dependOn(&run_objcopy_clashos.step);
7584
upload.dependOn(&run_send_image_tool.step);
7685
}

src/bootloader.ld

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
ENTRY(boot)
22

33
SECTIONS {
4-
kernelMainAt0x1100 = 0x1100; /* Must match value from kernel linker script */
4+
kernel_main = 0x80100; /* Must match value from kernel linker script */
55

66
. = 0x8800000; /* Must match debug.bootloader_address */
77

src/bootloader.zig

+10-5
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,24 @@ const builtin = @import("builtin");
1010
const debug = @import("debug.zig");
1111
const serial = @import("serial.zig");
1212

13-
export fn bootloader_main(start_addr: [*]u8, len: usize) linksection(".text.first") noreturn {
13+
export fn bootloaderMain(start_addr: [*]u8, len: usize) linksection(".text.first") noreturn {
1414
var i: usize = 0;
1515
while (i < len) : (i += 1) {
1616
start_addr[i] = serial.readByte();
1717
}
18+
19+
serial.log("Jumping to new code...\n", .{});
20+
while (!serial.isDoneWriting()) {}
1821
asm volatile (
19-
\\mov sp,#0x08000000
20-
\\bl kernelMainAt0x1100
22+
\\ mov sp,#0x08000000
23+
\\ eor x29, x29, x29
24+
\\ eor x30, x30, x30
25+
\\ b kernel_main
2126
);
2227
unreachable;
2328
}
2429

2530
pub fn panic(message: []const u8, stack_trace: ?*builtin.StackTrace) noreturn {
26-
serial.log("BOOTLOADER PANIC: {}", message);
27-
debug.wfe_hang();
31+
serial.log("BOOTLOADER PANIC: {}", .{message});
32+
debug.wfeHang();
2833
}

src/debug.zig

+37-21
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const builtin = @import("builtin");
22
const std = @import("std");
3+
const build_options = @import("build_options");
34
const assert = std.debug.assert;
45
const serial = @import("serial.zig");
56

@@ -15,40 +16,53 @@ extern var __debug_ranges_start: u8;
1516
extern var __debug_ranges_end: u8;
1617

1718
const source_files = [_][]const u8{
18-
"src/debug.zig",
19-
"src/video_core_mailboxes.zig",
19+
"src/main.zig",
2020
"src/bootloader.zig",
21+
"src/debug.zig",
2122
"src/serial.zig",
23+
"src/time.zig",
24+
"src/mmio.zig",
25+
"src/exceptions.zig",
2226
"src/video_core_metrics.zig",
2327
"src/video_core_properties.zig",
2428
"src/video_core_frame_buffer.zig",
25-
"src/time.zig",
26-
"src/main.zig",
27-
"src/mmio.zig",
29+
"src/video_core_mailboxes.zig",
2830
"src/slice_iterator.zig",
2931
};
3032

3133
var already_panicking: bool = false;
3234

33-
pub fn panic(stack_trace: ?*builtin.StackTrace, comptime fmt: []const u8, args: ...) noreturn {
35+
pub fn panic(stack_trace: ?*builtin.StackTrace, comptime fmt: []const u8, args: var) noreturn {
3436
@setCold(true);
3537
if (already_panicking) {
36-
serial.log("\npanicked during kernel panic");
37-
wfe_hang();
38+
serial.log("panicked during kernel panic!!\n", .{});
39+
wfeHang();
3840
}
3941
already_panicking = true;
4042

41-
serial.log("panic: " ++ fmt, args);
43+
serial.log("panic: " ++ fmt ++ "\n", args);
4244

4345
const first_trace_addr = @returnAddress();
44-
if (stack_trace) |t| {
45-
dumpStackTrace(t);
46+
if (build_options.want_stack_traces) {
47+
if (stack_trace) |t| {
48+
dumpStackTrace(t);
49+
}
50+
dumpCurrentStackTrace(first_trace_addr);
51+
} else {
52+
serial.log("Stack trace:\n", .{});
53+
var it = std.debug.StackIterator{ .first_addr = null, .fp = @frameAddress() };
54+
while (it.next()) |return_address| {
55+
if (return_address == 0) break;
56+
57+
serial.log(" 0x{x}\n", .{return_address - 4});
58+
}
59+
serial.log("The stack trace can be decoded using `addr2line -e zig-cache/clashos <address>`\n\n", .{});
60+
serial.log("To get stack traces during runtime, enable them using `zig build -Dstacktraces`\n", .{});
4661
}
47-
dumpCurrentStackTrace(first_trace_addr);
48-
wfe_hang();
62+
wfeHang();
4963
}
5064

51-
pub fn wfe_hang() noreturn {
65+
pub fn wfeHang() noreturn {
5266
while (true) {
5367
asm volatile ("wfe");
5468
}
@@ -116,7 +130,7 @@ fn getSelfDebugInfo() !*std.debug.DwarfInfo {
116130
.debug_info = dwarfSectionFromSymbol(&__debug_info_start, &__debug_info_end),
117131
.debug_abbrev = dwarfSectionFromSymbolAbs(&__debug_abbrev_start, &__debug_abbrev_end),
118132
.debug_str = dwarfSectionFromSymbolAbs(&__debug_str_start, &__debug_str_end),
119-
.debug_line = dwarfSectionFromSymbol(&__debug_line_start, &__debug_line_end),
133+
.debug_line = dwarfSectionFromSymbolAbs(&__debug_line_start, &__debug_line_end),
120134
.debug_ranges = dwarfSectionFromSymbolAbs(&__debug_ranges_start, &__debug_ranges_end),
121135
.abbrev_table_list = undefined,
122136
.compile_unit_list = undefined,
@@ -140,22 +154,22 @@ const kernel_panic_allocator = &kernel_panic_allocator_state.allocator;
140154

141155
pub fn dumpStackTrace(stack_trace: *const builtin.StackTrace) void {
142156
const dwarf_info = getSelfDebugInfo() catch |err| {
143-
serial.log("Unable to dump stack trace: Unable to open debug info: {}", @errorName(err));
157+
serial.log("Unable to dump stack trace: Unable to open debug info: {}", .{@errorName(err)});
144158
return;
145159
};
146160
writeStackTrace(stack_trace, dwarf_info) catch |err| {
147-
serial.log("Unable to dump stack trace: {}", @errorName(err));
161+
serial.log("Unable to dump stack trace: {}", .{@errorName(err)});
148162
return;
149163
};
150164
}
151165

152166
pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
153167
const dwarf_info = getSelfDebugInfo() catch |err| {
154-
serial.log("Unable to dump stack trace: Unable to open debug info: {}", @errorName(err));
168+
serial.log("Unable to dump stack trace: Unable to open debug info: {}", .{@errorName(err)});
155169
return;
156170
};
157171
writeCurrentStackTrace(dwarf_info, start_addr) catch |err| {
158-
serial.log("Unable to dump stack trace: {}", @errorName(err));
172+
serial.log("Unable to dump stack trace: {}", .{@errorName(err)});
159173
return;
160174
};
161175
}
@@ -189,15 +203,17 @@ fn printLineFromFile(out_stream: var, line_info: std.debug.LineInfo) anyerror!vo
189203
return;
190204
}
191205
}
192-
try out_stream.print("(source file {} not added in std/debug.zig)\n", line_info.file_name);
206+
try out_stream.print("(source file {} not added in std.debug.zig)\n", .{line_info.file_name});
193207
}
194208

195209
fn writeCurrentStackTrace(dwarf_info: *std.debug.DwarfInfo, start_addr: ?usize) !void {
196210
var it = std.debug.StackIterator.init(start_addr);
197211
while (it.next()) |return_address| {
212+
if (return_address == 0) break;
213+
198214
try dwarf_info.printSourceAtAddress(
199215
serial_out_stream,
200-
return_address,
216+
return_address - 4,
201217
true,
202218
printLineFromFile,
203219
);

src/exceptions.zig

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
const serial = @import("serial.zig");
2+
const debug = @import("debug.zig");
3+
4+
comptime {
5+
asm (
6+
\\.section .text
7+
\\.balign 0x800
8+
\\exception_vector_table:
9+
\\.balign 0x80
10+
\\ bl exceptionHandler
11+
\\.balign 0x80
12+
\\ bl exceptionHandler
13+
\\.balign 0x80
14+
\\ bl exceptionHandler
15+
\\.balign 0x80
16+
\\ bl exceptionHandler
17+
\\.balign 0x80
18+
\\ bl exceptionHandler
19+
\\.balign 0x80
20+
\\ bl exceptionHandler
21+
\\.balign 0x80
22+
\\ bl exceptionHandler
23+
\\.balign 0x80
24+
\\ bl exceptionHandler
25+
);
26+
}
27+
28+
pub fn init() void {
29+
asm volatile (
30+
\\ adr x0, exception_vector_table
31+
\\ msr vbar_el2,x0
32+
);
33+
}
34+
35+
export fn exceptionHandler() void {
36+
serial.log("arm exception taken\n", .{});
37+
var current_el = asm ("mrs %[current_el], CurrentEL\n"
38+
: [current_el] "=r" (-> usize)
39+
);
40+
serial.log("CurrentEL {x} exception level {}\n", .{ current_el, current_el >> 2 & 0x3 });
41+
var esr_el2 = asm ("mrs %[esr_el2], esr_el2"
42+
: [esr_el2] "=r" (-> usize)
43+
);
44+
serial.log("esr_el2 {x} code 0x{x}\n", .{ esr_el2, esr_el2 >> 26 & 0x3f });
45+
var elr_el2 = asm ("mrs %[elr_el2], elr_el2"
46+
: [elr_el2] "=r" (-> usize)
47+
);
48+
serial.log("elr_el2 {x}\n", .{elr_el2});
49+
var spsr_el2 = asm ("mrs %[spsr_el2], spsr_el2"
50+
: [spsr_el2] "=r" (-> usize)
51+
);
52+
serial.log("spsr_el2 {x}\n", .{spsr_el2});
53+
var far_el2 = asm ("mrs %[far_el2], far_el2"
54+
: [far_el2] "=r" (-> usize)
55+
);
56+
serial.log("far_el2 {x}\n", .{far_el2});
57+
serial.log("execution is now stopped in arm exception handler\n", .{});
58+
59+
debug.panic(null, "exception!!!", .{});
60+
}

src/linker.ld

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
ENTRY(_start)
22

33
SECTIONS {
4-
. = 0x0000;
4+
. = 0x80000;
55

66
.text : ALIGN(4K) {
77
KEEP(*(.text.boot))
88
__end_init = .;
9-
. = 0x1000;
10-
KEEP(*(.text.exception))
11-
. = 0x1100; /* Must match address from bootloader.ld */
9+
. = 0x80100; /* Must match address from bootloader.ld */
1210
KEEP(*(.text.main))
1311
*(.text)
1412
}
@@ -43,5 +41,5 @@ SECTIONS {
4341
__bss_end = .;
4442
}
4543

46-
bootloader_main = 0x8800000; /* Must match bootloader linker script */
44+
bootloaderMain = 0x8800000; /* Must match bootloader linker script */
4745
}

0 commit comments

Comments
 (0)