You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: content/rust-os/1-hello-riscv/index.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -13,7 +13,7 @@ series = ["rust-os"]
13
13
14
14
{% quote (class="info")%}
15
15
16
-
This is a series of posts about my journey creating a kernel in rust. You can find the code for this project [here](https://github.com/explodingcamera/pogos/tree/part-1) and all of the posts in this series [here](/series/os-dev/).
16
+
This is a series of posts about my journey creating a kernel in rust. You can find the code for this project [here](https://github.com/explodingcamera/pogos/tree/part-1) and all of the posts in this series [here](/series/rust-os/).
Copy file name to clipboardExpand all lines: content/rust-os/2-shell/index.md
+128-8Lines changed: 128 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,8 +1,8 @@
1
1
+++
2
2
transparent = true
3
-
title = "Creating a Kernel in Rust #2: Shell [Work in Progress]"
3
+
title = "Creating a Kernel in Rust #2: Shell"
4
4
description = "Creating a simple shell for our kernel to run commands and help us debug"
5
-
date = 2023-05-14
5
+
date = 2023-07-08
6
6
7
7
[taxonomies]
8
8
tags = ["rust", "riscv", "kernel"]
@@ -11,9 +11,7 @@ series = ["rust-os"]
11
11
12
12
{% quote (class="info")%}
13
13
14
-
This is a series of posts about my journey creating a kernel in rust. You can find the code for this project [here](https://github.com/explodingcamera/pogos/tree/part-1) and all of the posts in this series [here](/series/os-dev/).
15
-
16
-
This post isn't finished yet, but I wanted to get it out so I stop procrastinating on it. More content will follow next week.
14
+
This is a series of posts about my journey creating a kernel in rust. You can find the code for this project [here](https://github.com/explodingcamera/pogos/tree/part-2) and all of the posts in this series [here](/series/rust-os/).
17
15
18
16
{% end %}
19
17
@@ -24,7 +22,7 @@ Like I mentioned in the previous post, we can't yet use heap allocated data stru
24
22
25
23
{{toc}}
26
24
27
-
# Global Allocator
25
+
# Memory Allocators
28
26
29
27
To better understand what a global allocator is, we'll create a simple linear allocator that will allocate memory from a fixed size buffer. This allocator will only be able to allocate memory, not free it, but it will be enough to get us started.
30
28
@@ -67,7 +65,9 @@ impl LinearAllocator {
67
65
}
68
66
```
69
67
70
-
We'll also need to implement the `GlobalAlloc` trait, so Rust's `#[global_allocator]` compile built-in knows how to use our allocator. This trait has two methods: `alloc` and `dealloc`. We'll only implement `alloc` for now, since we won't be able to free memory until we implement a more complex allocator.
68
+
## Global Allocator
69
+
70
+
We'll also need to implement the `GlobalAlloc` trait, so Rust's `#[global_allocator]` compile time built-in knows how to use our allocator. This trait has two methods: `alloc` and `dealloc`. We'll only implement `alloc` for now, since we won't be able to free memory with our implementation.
71
71
72
72
The trait also requires that we mark our implementation as `unsafe` since we are dealing with raw pointers and memory addresses.
73
73
@@ -114,7 +114,7 @@ unsafe impl GlobalAlloc for LinearAllocator {
114
114
}
115
115
```
116
116
117
-
Now that we have our allocator, we can use it to allocate memory in our kernel. We'll start by allocating a buffer for it to use:
117
+
Before we can start using our allocator, we need to initialize it and allocate some memory for it to use. We'll do this in our `src/heap.rs` file:
118
118
119
119
{{ file(name = "src/heap.rs") }}
120
120
@@ -166,3 +166,123 @@ fn main(a0: usize) -> ! {
166
166
utils::shutdown();
167
167
}
168
168
```
169
+
170
+
Now, we can use our allocator to allocate some memory! Let's create a `Vec` and push some values to it to make sure everything works as expected:
171
+
172
+
{{ file(name = "src/main.rs") }}
173
+
174
+
```rust
175
+
176
+
letmutv=Vec::new();
177
+
178
+
v.push(1);
179
+
v.push(2);
180
+
v.push(3);
181
+
182
+
println!("{:?}", v);
183
+
184
+
// [1, 2, 3]
185
+
```
186
+
187
+
This is great, but we can't really do much with this allocator since we can't free memory. Alternative allocators are, for example, [linked list allocators](https://os.phil-opp.com/allocator-designs/#linked-list-allocator), [binary buddy allocators](https://www.kernel.org/doc/gorman/html/understand/understand009.html), and [slab allocators](https://www.kernel.org/doc/gorman/html/understand/understand011.html). We won't be implementing any of these in here, but I encourage you to read about them and try to implement them yourself! Some of these are also available as crates on crates.io to use them as drop-in replacements for our naive implementation here ([linked_list_allocator](https://crates.io/crates/linked-list-allocator), [buddy_system_allocator](https://crates.io/crates/buddy_system_allocator) and [slabmalloc](https://crates.io/crates/slabmalloc)).
188
+
189
+
# Shell
190
+
191
+
With most essential rust features available, we can now start working on our shell. This shell will allow us to interact with our kernel and inspect its state.
192
+
193
+
The shell will be a simple loop for now, that reads characters from SBIs `console_getchar` function, and executes some basic commands.
194
+
195
+
{{ file(name = "src/main.rs") }}
196
+
197
+
```rust
198
+
199
+
pubconstENTER:u8=13;
200
+
pubconstBACKSPACE:u8=127;
201
+
202
+
pubfnshell() {
203
+
print!("> ");
204
+
205
+
letmutcommand=String::new(); // finnaly, we can use heap allocated strings!
206
+
207
+
loop {
208
+
matchsbi::legacy::console_getchar() {
209
+
Some(ENTER) => {
210
+
println!();
211
+
process_command(&command);
212
+
command.clear();
213
+
print!("> ");
214
+
}
215
+
Some(BACKSPACE) => {
216
+
ifcommand.len() > 0 {
217
+
command.pop();
218
+
print!("{}", BACKSPACEaschar)
219
+
}
220
+
}
221
+
}
222
+
}
223
+
}
224
+
225
+
226
+
fnprocess_command(command:&str) {
227
+
matchcommand {
228
+
"help"|"?"|"h"=> {
229
+
println!("available commands:");
230
+
println!(" help print this help message (alias: h, ?)");
231
+
println!(" shutdown shutdown the machine (alias: sd, exit)");
232
+
}
233
+
"shutdown"|"sd"|"exit"=>util::shutdown(),
234
+
""=> {}
235
+
_=> {
236
+
println!("unknown command: {command}");
237
+
}
238
+
};
239
+
}
240
+
241
+
```
242
+
243
+
```
244
+
> help
245
+
available commands:
246
+
help print this help message (alias: h, ?)
247
+
shutdown shutdown the machine (alias: sd, exit)
248
+
> exit
249
+
```
250
+
251
+
## Debugging
252
+
253
+
Being able to shutdown the machine is great and all, but let's add some more functionality. We'll start by adding some commands to trigger different exceptions, so we can test our exception handler from the previous chapter.
254
+
255
+
{{ file(name = "src/main.rs") }}
256
+
257
+
```rust
258
+
259
+
matchcommand {
260
+
// ...
261
+
"pagefault"=> {
262
+
// read from an invalid address to trigger a page fault
// ebreak triggers a breakpoint exception, a trap that can be used for debugging with gdb or similar tools
267
+
unsafe { asm!("ebreak") };
268
+
}
269
+
// ...
270
+
}
271
+
```
272
+
273
+
When we now run the `pagefault` command, we'll see that - well - nothing happens. This is because we haven't set up a page table yet, and the kernel is still running in physical memory, something we want to change in the next chapter.
274
+
275
+
Our breakpoint command, however, will trigger a breakpoint exception, and we'll see the following output:
panicked at 'Exception cause: Exception(Breakpoint)', kernel/src/trap.rs:33:5
281
+
```
282
+
283
+
This is great, we can now see the trap frame and the exception cause, so we can start debugging our kernel! To make use of this, you can also use an external debugger like [gdb](https://www.gnu.org/software/gdb/), which allows you to set breakpoints, inspect memory, step through code, and much more.
284
+
Setting up gdb is a bit more involved, so if you run into issues check out this [guide](https://os.phil-opp.com/set-up-gdb/). I'm more of a print-debugging and hit my head against the wall until it works kind of guy, so I won't be covering this in detail here.
285
+
286
+
There's a lot of improvements we can make to this shell, for some ideas, check out my `simple_shell` crate on [crates.io](https://crates.io/crates/simple_shell). I recommend you try to implement some of these yourself, as it's a fun exercise. Something that might be helpful is this ANSI escape code [cheat sheet](https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797) for processing user input like arrow keys, backspace, etc.
287
+
288
+
After this fairly short chapter, we'll add multi-tasking to our kernel using async rust, so we can run multiple programs at the same time and explore paging and virtual memory.
0 commit comments