{% raw %}
Retep998

Please add #[repr(C)] to some of your structs, in particular the ones that depend on having a specific layout.

Philipp Oppermann

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)]?

Theo Belaire

You could also link to: http://embedded.hannobraun....
for another example of getting no_std rust working.

jeffreywilcke

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 :-)

Philipp Oppermann

Thanks for the hint! I removed that link and added the rest of the Color enum.

Btw: you accidentally skipped the number 2 in your Color enum :).

jeffreywilcke

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

Philipp Oppermann

I just checked out your project and color_code: self.color_code works fine. Maybe it was before you made ColorCode Copy?

Edit: Damn, I totally forgot to describe Copy and Clone…

Update: I mentioned the compiler errors and added some explanation here and here.

jeffreywilcke

Awesome, thanks! And thank you for the explanation.

emk1024

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.

Philipp Oppermann

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?

emk1024

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. :-)

Ahmed Charles

I think it should be 80 columns and 25 rows, when describing VGA.

Philipp Oppermann

Thanks, fixed it.

FreeFull

When you mention the write!/writeln! macros can be now used, the example uses Writer::new(...) although Writer doesn't have a `new` method.

Philipp Oppermann

Thanks for reporting! I created #118 for it.

Hunter Lester

Thank you for this education. I love your presentation.

Philipp Oppermann

You're welcome. Thanks a lot!

Nathan

The print macro should be `let mut writer`.

Philipp Oppermann

Thanks! Fixed in #156.

Chris Latham

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.

Andrii Zymohliad

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?

Philipp Oppermann

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!

Andrii Zymohliad

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" :)

Philipp Oppermann

Yes, you explain everything very well. Very surprisingly well for such difficult topic.

Thanks so much!

Hoschi

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);

Joshua Beitler

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?

Philipp Oppermann

As soon as the screen is full (i.e. scrolling begins) both approaches do the same thing: writing to the bottom line and shifting the other lines up. By starting at the bottom, we don't need any special case for the first 25 lines.

(…and maybe I just wanted to do things a bit differently :D)

Harry Rigg

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!

Philipp Oppermann

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!

ocamlmycaml

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!

ocamlmycaml

I figured it out - I had OUTPUT_FORMAT(elf32-i386) in my linker.ld file. I was trying a few things to run the kernel without making an iso

I'm not gonna pretend like I understand what happened, but removing that OUTPUT_FORMAT statement fixed my problems

Esdras

Solution for the problems of compilation:

1: go to vga_buffer.rs 2: go to line buffer: unsafe { Unique::new(0xb8000 as *mut _) }, 3: change for buffer: unsafe { Unique::new_unchecked(0xb8000 as *mut _) }, thanks for attention (sorry for my english)

Hello :)

Great tutorials, just a quick question for learning purposes. Could the values in enum be defined implicitly like so?

pub enum Color {
      Black,
      Blue,
      ...
   }
  
Philipp Oppermann

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).

Anonym

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!

Philipp Oppermann

Sure, why shouldn't it? The whole formatting part is handled by Rust's format_args!</code macro, so everything should work the same.

Anonym

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?

Philipp Oppermann

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.

Philipp Oppermann

Yes, it should work.

NateDogg1232

I keep getting the error of the trait `core::marker::Copy` is not implemented for `vga_buffer::ScreenChar`

Why exactly does this happen despite everything looking up to snuff?

Philipp Oppermann

You need to #[derive(Clone, Copy)] for ScreenChar.

{% endraw %}