You could also link to: http://embedded.hannobraun....
for another example of getting no_std rust working.
{% raw %}
You could also link to: http://embedded.hannobraun....
for another example of getting no_std rust working.
If anyone else (like me) is running in to some minor issues due to the incompleteness of the source code please have a look at my version that compiles: https://github.com/obscuren...
@phil_opp:disqus FYI the link up top that says (full file) is missing and comes up with an anchor to #TODO :-)
Ah oops :)
I'm fairly new to rust and I couldn't get https://github.com/obscuren... that line (in your original example) to work. Throwing something about "borrowed something something couldn't be moved".
Also thanks for the excellent examples and post! :D
I made it this far, and then started on implementing interrupts, which are needed for keyboard input. Along the way, I discovered the rust-x86 crate, which provides data structures for major x86_64 CPU data types. This looks like it would save a lot of debugging time and digging around in manuals. Also of interest is the hilarious and extremely helpful After 5 days, my OS doesn't crash when I press a key.
My interrupt-descriptor-table-in-progress is also available on GitHub for anybody who's interested in laying the groundwork for keyboard I/O.
This is definitely a fun idea, and your explanations are great. Highly recommended.
Thanks for the links! The rust-x86 crate looks useful indeed (but may need some minor additions/changes for 64bit). Julia Evans's blog is great and was my first resource about rust OS development :). Just keep in mind that it's from 2013 and thus uses an early version of Rust.
Your interrupt table code looks good! I think the handler functions need to be assembly functions until support for something like naked functions is added. Am I right?
Yup. x86 interrupt handler functions appear to be completely non-standard in any case—it seems like every x86(_64) OS I've looked at has different rules for saving registers, mostly because the architecture is such a mess of ancient hacks. I also have another comment-in-progress on the XMM registers, which cause serious headaches implementing interrupts.
rust-x86 appears to actually be x86_64-only, despite the name. The data structures I looked at were all what I'd expect on a 64-bit chip. I'll probably throw away a lot of my carefully debugged code and just use the rust-x86 versions of stuff.
It would be nice to have a bunch of crates on cargo which handle common processor and I/O stuff. And maybe a Rust-only ELF loader library for loading and relocating user-space binaries. :-)
When you mention the write!/writeln! macros can be now used, the example uses Writer::new(...) although Writer doesn't have a `new` method.
Thanks for reporting! I created #118 for it.
hey, great articles so far!
i've been following along, and i've run into some issues with the ::core::fmt::Write implementation for our writer class.
if i add that code in, i get these linker errors:
core.0.rs:(.text._ZN4core3fmt5write17hdac96890aec66a9aE+0x324): undefined reference to `_Unwind_Resume'
core.0.rs:(.text._ZN4core3fmt5write17hdac96890aec66a9aE+0x3eb): undefined reference to `_Unwind_Resume'
core.0.rs:(.text._ZN4core3fmt5write17hdac96890aec66a9aE+0x3f3): undefined reference to `_Unwind_Resume'
i've gone back and checked that i set panic to "abort" for both dev and release profiles in my config.toml, the same way you did to fix the unwinding issues. everything seems to match up with what you have. what have i missed?
thanks in advance.
It becomes more and more difficult for me to understand it... Mostly because of difficulty of Rust. I just follow every step and everything works for me, but I feel like if I make any step away from the instruction - everything will get broken.
But still I learn a lot from this blog. Thank you again!
One thing I can't understand: we implement ::core::fmt::Write trait for our Writer, and we implement 'write_str' method for it. But how can we use 'write_fmt' method if we didn't define it here?
The core::fmt::Write defines 3 methods: `write_str`, `write_char`, and `write_fmt`. However, the latter two have default implementations, so we only need to implement `write_str`.
I try to explain uncommon features of Rust in the posts, but providing a complete Rust introduction is out of scope. I recommend you to read the official Rust book or its new iteration (still work-in-progress).
It becomes more and more difficult for me to understand it...
That's unfortunate :(. Feel free to ask if something else is unclear!
Aa, ok. Thanks for clarification and quick reply.
Yes, you explain everything very well. Very surprisingly well for such difficult topic. Of course complete Rust intro is out of scope. But I didn't know about new iteration of Rust book. Thank you for the hint.
And thanks a lot for such welcoming and kind manner of communication with your "students" :)
Hey!
If you are interested in adding some build information, you can do it this way:
Add the following to your Makefile:
BUILD_NUMBER_FILE := buildno.txt
BUILD_NUMBER_LDFLAGS = --defsym _BUILD_NUMBER=$$(cat $(BUILD_NUMBER_FILE)) --defsym _BUILD_DATE=$$(date +'%Y%m%d')
$(kernel): builddata cargo ..
builddata:
touch $(BUILD_NUMBER_FILE)
@echo $$(($$(cat $(BUILD_NUMBER_FILE)) + 1)) > $(BUILD_NUMBER_FILE)
The above will make the current date and a build number available in the kernel, incremented each time you build it.
To access build information:
Add the following to your lib.rs:
extern {
fn _BUILD_NUMBER();
fn _BUILD_DATE();
}
Now you can do the following, for example in your rust_main():
let build_number = _BUILD_NUMBER as u32;
let build_date = _BUILD_DATE as u32;
println!("Build {}: {} ", build_number, build_date);
I'm curious - why does this code write from the bottom of the screen and then up, when most other VGA drivers go from the top of the screen down?
I went ahead and made the "panic_fmt" function complete :) Now I get filename, line and an error message when rust panics! very helpful, here is the code:
#[lang = "panic_fmt"]
extern fn panic_fmt(args: fmt::Arguments, file: &'static str, line: u32) -> ! {
println!("Panic in file {} line {}: {}", file, line, args);
loop {}
}
Thanks for the awesome tutorials!
Great! We also do this in the next post, but it's a good idea to mention it here, too.
Thanks for the code by the way, I always thought that the `file` argument is of type `&str` (instead of `&'static str`). I opened #256 to fix this.
Thanks for the awesome tutorials!
You're welcome, I'm glad you like them!
So i'm trying to make `println!("{}: some number", 1);` work, but when I add that line to my rust_main function, the emulator does the whole triple exception thing starting with a 0xd error - which according to OSDev.org is a "General protection fault":
```check_exception old: 0xffffffff new 0xd
0: v=0d e=0000 i=0 cpl=0 IP=0008:ec834853e5894855 pc=ec834853e5894855 SP=0010:000000000012ec18 env->regs[R_EAX]=0000000000000a00```
`println!("Hello {}!", "world");` works just fine - it just doesn't seem to be able to interpolate non-string types. Would you have any idea on what's going wrong? I'm not sure where to even look. If you'd like to clone and run my code and take a look: https://github.com/ocamlmycaml/rust-moss/
btw ++good tutorial, i'm learning a lot!
Great tutorials, just a quick question for learning purposes.
Could the values in enum be defined implicitly like so?
pub enum Color {
Black,
Blue,
...
}
I don't know actually. It seems to work: https://play.rust-lang.org/?gist=7e8684f332ece651836c80b4d7439c1c&version=stable. However, I'm not sure if this is specified behavior or if it might be changed some day (e.g. by new compiler optimizations).
I followed everything in this tutorial to the letter, and had a question. If I were to try to print a string to the screen, how would I do it? I have been using
print!("{}", string)
with string containing what I want to print. I know this works in normal Rust, but would it work with the VGA buffer you made? Thanks!
Question. How would I go about changing the color of the text on the fly? Like if I wanted to print Hello World
and have "Hello" be green and "World" be white. How would I go about doing this?
You could use two separate print statements and change the color on the global writer in between. Or alternatively, you could add support for ANSI escape codes like in a normal terminal.
So would this work? https://repl.it/repls/NonstopCompleteCodes
Please add #[repr(C)] to some of your structs, in particular the ones that depend on having a specific layout.
Thanks for the hint! On `ScreenChar` it is definitely required. But I'm not quite sure if it's needed for the `ColorCode` newtype…
Where (else) would you add #[repr(C)]?