From 6e5ebc4bd9837f9300bb565cd60b330bc5c0bba8 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 18 Nov 2018 13:44:21 +0100 Subject: [PATCH 01/12] Transition the code to Rust 2018 --- Cargo.toml | 1 + src/bin/test-basic-boot.rs | 6 +----- src/bin/test-exception-breakpoint.rs | 7 +------ ...test-exception-double-fault-stack-overflow.rs | 7 +------ src/bin/test-panic.rs | 5 +---- src/interrupts.rs | 4 ++-- src/lib.rs | 16 +--------------- src/main.rs | 6 ++---- src/serial.rs | 6 +++--- src/vga_buffer.rs | 6 +++--- 10 files changed, 16 insertions(+), 48 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 730970f8..14c71a44 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ authors = ["Philipp Oppermann "] name = "blog_os" version = "0.2.0" +edition = "2018" [dependencies] bootloader = "0.3.4" diff --git a/src/bin/test-basic-boot.rs b/src/bin/test-basic-boot.rs index 9b891aad..f4acda07 100644 --- a/src/bin/test-basic-boot.rs +++ b/src/bin/test-basic-boot.rs @@ -2,11 +2,7 @@ #![cfg_attr(not(test), no_main)] // disable all Rust-level entry points #![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))] -// add the library as dependency (same crate name as executable) -#[macro_use] -extern crate blog_os; - -use blog_os::exit_qemu; +use blog_os::{exit_qemu, serial_println}; use core::panic::PanicInfo; /// This function is the entry point, since the linker looks for a function diff --git a/src/bin/test-exception-breakpoint.rs b/src/bin/test-exception-breakpoint.rs index 5f6db24c..fc3091e4 100644 --- a/src/bin/test-exception-breakpoint.rs +++ b/src/bin/test-exception-breakpoint.rs @@ -3,12 +3,7 @@ #![cfg_attr(not(test), no_main)] #![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))] -#[macro_use] -extern crate blog_os; -extern crate x86_64; -extern crate lazy_static; - -use blog_os::exit_qemu; +use blog_os::{exit_qemu, serial_println}; use core::panic::PanicInfo; use core::sync::atomic::{AtomicUsize, Ordering}; use lazy_static::lazy_static; diff --git a/src/bin/test-exception-double-fault-stack-overflow.rs b/src/bin/test-exception-double-fault-stack-overflow.rs index 673a8037..3d934a4c 100644 --- a/src/bin/test-exception-double-fault-stack-overflow.rs +++ b/src/bin/test-exception-double-fault-stack-overflow.rs @@ -3,12 +3,7 @@ #![cfg_attr(not(test), no_main)] #![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))] -#[macro_use] -extern crate blog_os; -extern crate x86_64; -extern crate lazy_static; - -use blog_os::exit_qemu; +use blog_os::{exit_qemu, serial_println}; use core::panic::PanicInfo; use lazy_static::lazy_static; diff --git a/src/bin/test-panic.rs b/src/bin/test-panic.rs index 64131b86..62ed7d7f 100644 --- a/src/bin/test-panic.rs +++ b/src/bin/test-panic.rs @@ -2,10 +2,7 @@ #![cfg_attr(not(test), no_main)] #![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))] -#[macro_use] -extern crate blog_os; - -use blog_os::exit_qemu; +use blog_os::{exit_qemu, serial_println}; use core::panic::PanicInfo; #[cfg(not(test))] diff --git a/src/interrupts.rs b/src/interrupts.rs index 728e6853..eec59ab6 100644 --- a/src/interrupts.rs +++ b/src/interrupts.rs @@ -4,7 +4,7 @@ // problem we skip compilation of this module on Windows. #![cfg(not(windows))] -use gdt; +use crate::gdt; use pic8259_simple::ChainedPics; use spin; use x86_64::structures::idt::{ExceptionStackFrame, InterruptDescriptorTable}; @@ -48,7 +48,7 @@ extern "x86-interrupt" fn double_fault_handler( stack_frame: &mut ExceptionStackFrame, _error_code: u64, ) { - use hlt_loop; + use crate::hlt_loop; println!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame); hlt_loop(); diff --git a/src/lib.rs b/src/lib.rs index 31a702a6..e806c7c8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,20 +1,6 @@ -#![no_std] // don't link the Rust standard library +#![cfg_attr(not(test), no_std)] // don't link the Rust standard library #![feature(abi_x86_interrupt)] -extern crate bootloader; -extern crate spin; -extern crate volatile; -extern crate lazy_static; -extern crate pic8259_simple; -extern crate uart_16550; -extern crate x86_64; -extern crate pc_keyboard; - -#[cfg(test)] -extern crate array_init; -#[cfg(test)] -extern crate std; - #[macro_use] pub mod vga_buffer; pub mod gdt; diff --git a/src/main.rs b/src/main.rs index c9890b1b..004c9dc5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,12 +2,10 @@ #![cfg_attr(not(test), no_main)] // disable all Rust-level entry points #![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))] -#[macro_use] -extern crate blog_os; -extern crate x86_64; - use core::panic::PanicInfo; +use blog_os::println; + /// This function is the entry point, since the linker looks for a function /// named `_start` by default. #[cfg(not(test))] diff --git a/src/serial.rs b/src/serial.rs index 494273c9..b89b843a 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -33,7 +33,7 @@ macro_rules! serial_print { /// Prints to the host through the serial interface, appending a newline. #[macro_export] macro_rules! serial_println { - () => (serial_print!("\n")); - ($fmt:expr) => (serial_print!(concat!($fmt, "\n"))); - ($fmt:expr, $($arg:tt)*) => (serial_print!(concat!($fmt, "\n"), $($arg)*)); + () => ($crate::serial_print!("\n")); + ($fmt:expr) => ($crate::serial_print!(concat!($fmt, "\n"))); + ($fmt:expr, $($arg:tt)*) => ($crate::serial_print!(concat!($fmt, "\n"), $($arg)*)); } diff --git a/src/vga_buffer.rs b/src/vga_buffer.rs index 9c245190..e63c4077 100644 --- a/src/vga_buffer.rs +++ b/src/vga_buffer.rs @@ -157,9 +157,9 @@ macro_rules! print { /// Like the `print!` macro in the standard library, but prints to the VGA text buffer. #[macro_export] macro_rules! println { - () => (print!("\n")); - ($fmt:expr) => (print!(concat!($fmt, "\n"))); - ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); + () => ($crate::print!("\n")); + ($fmt:expr) => ($crate::print!(concat!($fmt, "\n"))); + ($fmt:expr, $($arg:tt)*) => ($crate::print!(concat!($fmt, "\n"), $($arg)*)); } /// Prints the given formatted string to the VGA text buffer through the global `WRITER` instance. From fc4542463b190c8ec16346da17e616297e55ee84 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 18 Nov 2018 13:59:31 +0100 Subject: [PATCH 02/12] Rename vga_buffer::print function to avoid name clash with macro --- src/vga_buffer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vga_buffer.rs b/src/vga_buffer.rs index e63c4077..887d0917 100644 --- a/src/vga_buffer.rs +++ b/src/vga_buffer.rs @@ -151,7 +151,7 @@ impl fmt::Write for Writer { /// Like the `print!` macro in the standard library, but prints to the VGA text buffer. #[macro_export] macro_rules! print { - ($($arg:tt)*) => ($crate::vga_buffer::print(format_args!($($arg)*))); + ($($arg:tt)*) => ($crate::vga_buffer::_print(format_args!($($arg)*))); } /// Like the `print!` macro in the standard library, but prints to the VGA text buffer. @@ -163,7 +163,7 @@ macro_rules! println { } /// Prints the given formatted string to the VGA text buffer through the global `WRITER` instance. -pub fn print(args: fmt::Arguments) { +pub fn _print(args: fmt::Arguments) { use core::fmt::Write; use x86_64::instructions::interrupts; From 24e7974d04c4f6610fcdfc2d490e3f5b60a109e2 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 18 Nov 2018 14:30:40 +0100 Subject: [PATCH 03/12] Update to a newer println definition --- src/vga_buffer.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vga_buffer.rs b/src/vga_buffer.rs index 887d0917..729241c6 100644 --- a/src/vga_buffer.rs +++ b/src/vga_buffer.rs @@ -154,12 +154,11 @@ macro_rules! print { ($($arg:tt)*) => ($crate::vga_buffer::_print(format_args!($($arg)*))); } -/// Like the `print!` macro in the standard library, but prints to the VGA text buffer. +/// Like the `println!` macro in the standard library, but prints to the VGA text buffer. #[macro_export] macro_rules! println { () => ($crate::print!("\n")); - ($fmt:expr) => ($crate::print!(concat!($fmt, "\n"))); - ($fmt:expr, $($arg:tt)*) => ($crate::print!(concat!($fmt, "\n"), $($arg)*)); + ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); } /// Prints the given formatted string to the VGA text buffer through the global `WRITER` instance. From 767fd53d5f286b79fc2ee92638b423c5bbc3becf Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 18 Nov 2018 14:59:56 +0100 Subject: [PATCH 04/12] Remove macro_use and use explicit imports --- src/interrupts.rs | 2 +- src/lib.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/interrupts.rs b/src/interrupts.rs index eec59ab6..d9394d5c 100644 --- a/src/interrupts.rs +++ b/src/interrupts.rs @@ -4,7 +4,7 @@ // problem we skip compilation of this module on Windows. #![cfg(not(windows))] -use crate::gdt; +use crate::{gdt, print, println}; use pic8259_simple::ChainedPics; use spin; use x86_64::structures::idt::{ExceptionStackFrame, InterruptDescriptorTable}; diff --git a/src/lib.rs b/src/lib.rs index e806c7c8..d2a1d0ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,6 @@ #![cfg_attr(not(test), no_std)] // don't link the Rust standard library #![feature(abi_x86_interrupt)] -#[macro_use] pub mod vga_buffer; pub mod gdt; pub mod interrupts; From ba31b2166180d116ccbbc538525468afef0e7df0 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 18 Nov 2018 14:31:32 +0100 Subject: [PATCH 05/12] Update the VGA buffer post to the new println macro and Rust 2018 --- .../posts/03-vga-text-buffer/index.md | 43 +++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/blog/content/second-edition/posts/03-vga-text-buffer/index.md b/blog/content/second-edition/posts/03-vga-text-buffer/index.md index b6395148..f7df65c6 100644 --- a/blog/content/second-edition/posts/03-vga-text-buffer/index.md +++ b/blog/content/second-edition/posts/03-vga-text-buffer/index.md @@ -577,25 +577,29 @@ Now that we have a global writer, we can add a `println` macro that can be used [`println!` macro]: https://doc.rust-lang.org/nightly/std/macro.println!.html ```rust +#[macro_export] macro_rules! println { () => (print!("\n")); - ($fmt:expr) => (print!(concat!($fmt, "\n"))); - ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); + ($($arg:tt)*) => (print!("{}\n", format_args!($($arg)*))); } ``` -Macros are defined through one or more rules, which are similar to `match` arms. The `println` macro has three rules: The first rule for is invocations without arguments (e.g `println!()`), the second rule is for invocations with a single argument (e.g. `println!("Hello")`) and the third rule is for invocations with additional parameters (e.g. `println!("{}{}", 4, 2)`). -First line just prints a `\n` symbol which literally means "don't print anything, just break the line". -Last two rules simply append a newline character (`\n`) to the format string and then invoke the [`print!` macro], which is defined as: +Macros are defined through one or more rules, which are similar to `match` arms. The `println` macro has two rules: The first rule for is invocations without arguments (e.g `println!()`), which is expanded to `print!("\n)` and thus just prints a newline. the second rule is for invocations with parameters such as `println!("Hello")` or `println!("Number: {}", 4)`. It is also expanded to an invokation of the `print!` macro, passing all arguments and an additional newline `\n` at the end. + +The `#[macro_export]` attribute makes the available to the whole crate (not just the module it is defined) and external crates. It also places the macro at the crate root, which means that we have to import the macro through `use std::println` instead of `std::macros::println`. + +The [`print!` macro] is defined as: [`print!` macro]: https://doc.rust-lang.org/nightly/std/macro.print!.html ```rust +#[macro_export] macro_rules! print { ($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*))); } ``` -The macro expands to a call of the [`_print` function] in the `io` module. The [`$crate` variable] ensures that the macro also works from outside the `std` crate. For example, it expands to `::std` when it's used in other crates. + +The macro expands to a call of the [`_print` function] in the `io` module. The [`$crate` variable] ensures that the macro also works from outside the `std` crate by expanding to `std` when it's used in other crates. The [`format_args` macro] builds a [fmt::Arguments] type from the passed arguments, which is passed to `_print`. The [`_print` function] of libstd calls `print_to`, which is rather complicated because it supports different `Stdout` devices. We don't need that complexity since we just want to print to the VGA buffer. @@ -604,36 +608,41 @@ The [`format_args` macro] builds a [fmt::Arguments] type from the passed argumen [`format_args` macro]: https://doc.rust-lang.org/nightly/std/macro.format_args.html [fmt::Arguments]: https://doc.rust-lang.org/nightly/core/fmt/struct.Arguments.html -To print to the VGA buffer, we just copy the `println!` and `print!` macros, but modify them to use a `print` function: +To print to the VGA buffer, we just copy the `println!` and `print!` macros, but modify them to use our own `_print` function: ```rust // in src/vga_buffer.rs +#[macro_export] macro_rules! print { - ($($arg:tt)*) => ($crate::vga_buffer::print(format_args!($($arg)*))); + ($($arg:tt)*) => ($crate::vga_buffer::_print(format_args!($($arg)*))); } +#[macro_export] macro_rules! println { - () => (print!("\n")); - ($fmt:expr) => (print!(concat!($fmt, "\n"))); - ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); + () => ($crate::print!("\n")); + ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); } -pub fn print(args: fmt::Arguments) { +pub fn _print(args: fmt::Arguments) { use core::fmt::Write; WRITER.lock().write_fmt(args).unwrap(); } ``` -The `print` function locks our static `WRITER` and calls the `write_fmt` method on it. This method is from the `Write` trait, we need to import that trait. The additional `unwrap()` at the end panics if printing isn't successful. But since we always return `Ok` in `write_str`, that should not happen. + +The `_print` function locks our static `WRITER` and calls the `write_fmt` method on it. This method is from the `Write` trait, we need to import that trait. The additional `unwrap()` at the end panics if printing isn't successful. But since we always return `Ok` in `write_str`, that should not happen. + +We also add the `#[macro_export]` attribute to both macros to make them available everywhere in our crate. Note that this places the macros in the root namespace of the crate, so importing them via `use crate::vga_buffer::println` does not work. Instead, we have to do `use crate::println`. + +One thing that we changed from the original definitions is that we prefixed the invocations of the `print!` macro with `$crate` too. This ensures that we don't need to have to import the `print!` macro too if we only want to use `println`. ### Hello World using `println` -To use `println` in `main.rs`, we need to import the macros of the VGA buffer module first. Therefore we add a `#[macro_use]` attribute to the module declaration: +Now we can use `println` in our `_start` function: ```rust // in src/main.rs -#[macro_use] -mod vga_buffer; +use crate::println; #[no_mangle] pub extern "C" fn _start() { @@ -643,7 +652,7 @@ pub extern "C" fn _start() { } ``` -Since we imported the macros at crate level, they are available in all modules and thus provide an easy and safe interface to the VGA buffer. +We have to explicitly import the `println` macro in order to use it. This is different from the standard library where the `println` macro is implicitly imported. Note that the macros live directly under the crate root (i.e. `crate::println` instead of `crate::vga_buffer::println`) because of the `#[macro_export]` attribute. As expected, we now see a _“Hello World!”_ on the screen: From bf413d3baa9db2e0b98a5a31fe80b280e949e314 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 18 Nov 2018 15:00:52 +0100 Subject: [PATCH 06/12] Mention in the first post that our crate uses the 2018 edition --- .../posts/01-freestanding-rust-binary/index.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/blog/content/second-edition/posts/01-freestanding-rust-binary/index.md b/blog/content/second-edition/posts/01-freestanding-rust-binary/index.md index c68bb482..4ca80393 100644 --- a/blog/content/second-edition/posts/01-freestanding-rust-binary/index.md +++ b/blog/content/second-edition/posts/01-freestanding-rust-binary/index.md @@ -52,10 +52,12 @@ By default, all Rust crates link the [standard library], which depends on the op We start by creating a new cargo application project. The easiest way to do this is through the command line: ``` -> cargo new blog_os --bin +> cargo new blog_os --bin --edition 2018 ``` -I named the project `blog_os`, but of course you can choose your own name. The `--bin` flag specifies that we want to create an executable binary (in contrast to a library). When we run the command, cargo creates the following directory structure for us: +I named the project `blog_os`, but of course you can choose your own name. The `--bin` flag specifies that we want to create an executable binary (in contrast to a library) and the `--edition 2018` flag specifies that we want to use the [2018 edition] of Rust for our crate. When we run the command, cargo creates the following directory structure for us: + +[2018 edition]: https://rust-lang-nursery.github.io/edition-guide/rust-2018/index.html ``` blog_os From f5aea8f0159fa195811a88d02024ca5eeab2863f Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 18 Nov 2018 15:16:19 +0100 Subject: [PATCH 07/12] Update testing posts --- .../posts/04-unit-testing/index.md | 16 +++--- .../posts/05-integration-tests/index.md | 51 +++++-------------- src/main.rs | 2 +- src/serial.rs | 4 +- 4 files changed, 24 insertions(+), 49 deletions(-) diff --git a/blog/content/second-edition/posts/04-unit-testing/index.md b/blog/content/second-edition/posts/04-unit-testing/index.md index 7d6f80cc..a98a2848 100644 --- a/blog/content/second-edition/posts/04-unit-testing/index.md +++ b/blog/content/second-edition/posts/04-unit-testing/index.md @@ -111,13 +111,13 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out The test framework seems to work as intended. We don't have any tests yet, but we already get a test result summary. ### Silencing the Warnings -We get a few warnings about unused items, because we no longer compile our `_start` function. To silence such unused code warnings, we can add the following to the top of our `main.rs`: +We get a few warnings about unused imports, because we no longer compile our `_start` function. To silence such unused code warnings, we can add the following to the top of our `main.rs`: ``` -#![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))] +#![cfg_attr(test, allow(unused_imports))] ``` -Like before, the `cfg_attr` attribute sets the passed attribute if the passed condition holds. Here, we set the `allow(…)` attribute when compiling in test mode. We use the `allow` attribute to disable warnings for the `dead_code`, `unused_macro`, and `unused_import` _lints_. +Like before, the `cfg_attr` attribute sets the passed attribute if the passed condition holds. Here, we set the `allow(…)` attribute when compiling in test mode. We use the `allow` attribute to disable warnings for the `unused_import` _lint_. Lints are classes of warnings, for example `dead_code` for unused code or `missing-docs` for missing documentation. Lints can be set to four different states: @@ -131,15 +131,13 @@ Some lints are `allow` by default (such as `missing-docs`), others are `warn` by [clippy]: https://github.com/rust-lang-nursery/rust-clippy ### Including the Standard Library -Unit tests run on the host machine, so it's possible to use the complete standard library inside them. To link the standard library in test mode, we can add the following to our `main.rs`: +Unit tests run on the host machine, so it's possible to use the complete standard library inside them. To link the standard library in test mode, we can make the `#![no_std]` attribute conditional through `cfg_attr` too: -```rust -#[cfg(test)] -extern crate std; +```diff +-#![no_std] ++#![cfg_attr(not(test), no_std)] ``` -Rust knows where to find the `std` crate, so no modification to the `Cargo.toml` is required. - ## Testing the VGA Module Now that we have set up the test framework, we can add a first unit test for our `vga_buffer` module: diff --git a/blog/content/second-edition/posts/05-integration-tests/index.md b/blog/content/second-edition/posts/05-integration-tests/index.md index 7b2a6232..2e38b415 100644 --- a/blog/content/second-edition/posts/05-integration-tests/index.md +++ b/blog/content/second-edition/posts/05-integration-tests/index.md @@ -103,12 +103,13 @@ Like with the [VGA text buffer][vga lazy-static], we use `lazy_static` and a spi To make the serial port easily usable, we add `serial_print!` and `serial_println!` macros: ```rust -pub fn print(args: ::core::fmt::Arguments) { +pub fn _print(args: ::core::fmt::Arguments) { use core::fmt::Write; SERIAL1.lock().write_fmt(args).expect("Printing to serial failed"); } /// Prints to the host through the serial interface. +#[macro_export] macro_rules! serial_print { ($($arg:tt)*) => { $crate::serial::print(format_args!($($arg)*)); @@ -116,10 +117,11 @@ macro_rules! serial_print { } /// Prints to the host through the serial interface, appending a newline. +#[macro_export] macro_rules! serial_println { - () => (serial_print!("\n")); - ($fmt:expr) => (serial_print!(concat!($fmt, "\n"))); - ($fmt:expr, $($arg:tt)*) => (serial_print!(concat!($fmt, "\n"), $($arg)*)); + () => ($crate::serial_print!("\n")); + ($fmt:expr) => ($crate::serial_print!(concat!($fmt, "\n"))); + ($fmt:expr, $($arg:tt)*) => ($crate::serial_print!(concat!($fmt, "\n"), $($arg)*)); } ``` @@ -132,7 +134,8 @@ Now we can print to the serial interface in our `main.rs`: ```rust // in src/main.rs -#[macro_use] +use crate::serial_println; + mod serial; #[cfg(not(test))] @@ -145,7 +148,7 @@ pub extern "C" fn _start() -> ! { } ``` -Note that we need to add the `#[macro_use]` attribute to the `mod serial` declaration, because otherwise the `serial_println` macro is not imported. +Note that the `serial_println` macro lives directly under the root namespace because we used the `#[macro_export]` attribute, so importing it through `use crate::serial::serial_println` will not work. ### QEMU Arguments @@ -371,7 +374,7 @@ Cargo supports hybrid projects that are both a library and a binary. We only nee ```rust // src/lib.rs -#![no_std] // don't link the Rust standard library +#![cfg_attr(not(test), no_std)] // don't link the Rust standard library extern crate bootloader; extern crate spin; @@ -382,8 +385,6 @@ extern crate x86_64; #[cfg(test)] extern crate array_init; -#[cfg(test)] -extern crate std; // NEW: We need to add `pub` here to make them accessible from the outside pub mod vga_buffer; @@ -405,10 +406,10 @@ pub unsafe fn exit_qemu() { #![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))] // NEW: Add the library as dependency (same crate name as executable) -#[macro_use] extern crate blog_os; use core::panic::PanicInfo; +use blog_os::println; /// This function is the entry point, since the linker looks for a function /// named `_start` by default. @@ -429,29 +430,7 @@ fn panic(info: &PanicInfo) -> ! { } ``` -So we move everything except `_start` and `panic` to `lib.rs`, make the `vga_buffer` and `serial` modules public, and add an `extern crate` definition to our `main.rs`. - -This doesn't compile yet, because Rust's macros are not exported over crate boundaries by default. To export our printing macros, we need to add the `#[macro_export]` attribute to them: - -```rust -// in src/vga_buffer.rs - -#[macro_export] -macro_rules! print {…} - -#[macro_export] -macro_rules! println {…} - -// in src/serial.rs - -#[macro_export] -macro_rules! serial_print {…} - -#[macro_export] -macro_rules! serial_println {…} -``` - -Now everything should work exactly as before, including `bootimage run` and `cargo test`. +So we move everything except `_start` and `panic` to `lib.rs`, make the `vga_buffer` and `serial` modules public, and add an `extern crate` definition to our `main.rs`. Everything should work exactly as before, including `bootimage run` and `cargo test`. ### Test Basic Boot @@ -465,11 +444,10 @@ We are finally able to create our first integration test executable. We start si #![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))] // add the library as dependency (same crate name as executable) -#[macro_use] extern crate blog_os; use core::panic::PanicInfo; -use blog_os::exit_qemu; +use blog_os::{exit_qemu, serial_println}; /// This function is the entry point, since the linker looks for a function /// named `_start` by default. @@ -529,11 +507,10 @@ To test that our panic handler is really invoked on a panic, we create a `test-p #![cfg_attr(not(test), no_main)] #![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))] -#[macro_use] extern crate blog_os; use core::panic::PanicInfo; -use blog_os::exit_qemu; +use blog_os::{exit_qemu, serial_println}; #[cfg(not(test))] #[no_mangle] diff --git a/src/main.rs b/src/main.rs index 004c9dc5..acf8e667 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ #![no_std] // don't link the Rust standard library #![cfg_attr(not(test), no_main)] // disable all Rust-level entry points -#![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))] +#![cfg_attr(test, allow(unused_imports))] use core::panic::PanicInfo; diff --git a/src/serial.rs b/src/serial.rs index b89b843a..ccab77ec 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -10,7 +10,7 @@ lazy_static! { }; } -pub fn print(args: ::core::fmt::Arguments) { +pub fn _print(args: ::core::fmt::Arguments) { use core::fmt::Write; use x86_64::instructions::interrupts; @@ -26,7 +26,7 @@ pub fn print(args: ::core::fmt::Arguments) { #[macro_export] macro_rules! serial_print { ($($arg:tt)*) => { - $crate::serial::print(format_args!($($arg)*)); + $crate::serial::_print(format_args!($($arg)*)); }; } From 1d4cbdbe57affa2545b055e44caa74541273446d Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 18 Nov 2018 15:19:49 +0100 Subject: [PATCH 08/12] Update CPU exceptions post --- .../posts/06-cpu-exceptions/index.md | 41 +------------------ .../posts/08-hardware-interrupts/index.md | 6 +-- 2 files changed, 5 insertions(+), 42 deletions(-) diff --git a/blog/content/second-edition/posts/06-cpu-exceptions/index.md b/blog/content/second-edition/posts/06-cpu-exceptions/index.md index c6acc5cf..b3ddbb24 100644 --- a/blog/content/second-edition/posts/06-cpu-exceptions/index.md +++ b/blog/content/second-edition/posts/06-cpu-exceptions/index.md @@ -234,6 +234,7 @@ For our use case, we don't need to overwrite any instructions. Instead, we just // in src/interrupts.rs use x86_64::structures::idt::{InterruptDescriptorTable, ExceptionStackFrame}; +use crate::println; pub fn init_idt() { let mut idt = InterruptDescriptorTable::new(); @@ -249,7 +250,7 @@ extern "x86-interrupt" fn breakpoint_handler( Our handler just outputs a message and pretty-prints the exception stack frame. -When we try to compile it, the following errors occur: +When we try to compile it, the following error occurs: ``` error[E0658]: x86-interrupt ABI is experimental and subject to change (see issue #40180) @@ -265,44 +266,6 @@ error[E0658]: x86-interrupt ABI is experimental and subject to change (see issue This error occurs because the `x86-interrupt` calling convention is still unstable. To use it anyway, we have to explicitly enable it by adding `#![feature(abi_x86_interrupt)]` on the top of our `lib.rs`. -``` -error: cannot find macro `println!` in this scope - --> src/interrupts.rs:40:5 - | -40 | println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame); - | ^^^^^^^ - | - = help: have you added the `#[macro_use]` on the module/import? -``` - -This happened because we forgot to add `#[macro_use]` before our import of the `vga_buffer` module. - -```rust -// in src/lib.rs - -pub mod gdt; -pub mod interrupts; -pub mod serial; -#[macro_use] // new -pub mod vga_buffer; -``` - -However, after adding `#[macro_use]` before the module import, we still get the same error. Sometimes this can be confusing, but it's actually a quirk of how Rust's macro system works. Macros _must be defined_ before you can use them. This is one case where import order matters in Rust. We can easily fix this by ensuring the order of our imports places the macros first: - -```rust -// in src/lib.rs - -#[macro_use] -pub mod vga_buffer; // import this before other modules so its macros may be used -pub mod gdt; -pub mod interrupts; -pub mod serial; -``` - -Now we can use our `print!` and `println!` macros in `interrupts.rs`. If you'd like to know more about the ins and outs of macros and how they differ from functions [you can find more information here][in-depth-rust-macros]. - -[in-depth-rust-macros]: https://doc.rust-lang.org/book/second-edition/appendix-04-macros.html - ### Loading the IDT In order that the CPU uses our new interrupt descriptor table, we need to load it using the [`lidt`] instruction. The `InterruptDescriptorTable` struct of the `x86_64` provides a [`load`][InterruptDescriptorTable::load] method function for that. Let's try to use it: diff --git a/blog/content/second-edition/posts/08-hardware-interrupts/index.md b/blog/content/second-edition/posts/08-hardware-interrupts/index.md index bab486a8..58175f31 100644 --- a/blog/content/second-edition/posts/08-hardware-interrupts/index.md +++ b/blog/content/second-edition/posts/08-hardware-interrupts/index.md @@ -253,7 +253,7 @@ We can already provoke a deadlock in our kernel. Remember, our `println` macro c […] -pub fn print(args: fmt::Arguments) { +pub fn _print(args: fmt::Arguments) { use core::fmt::Write; WRITER.lock().write_fmt(args).unwrap(); } @@ -308,7 +308,7 @@ To avoid this deadlock, we can disable interrupts as long as the `Mutex` is lock /// Prints the given formatted string to the VGA text buffer /// through the global `WRITER` instance. -pub fn print(args: fmt::Arguments) { +pub fn _print(args: fmt::Arguments) { use core::fmt::Write; use x86_64::instructions::interrupts; // new @@ -328,7 +328,7 @@ We can apply the same change to our serial printing function to ensure that no d ```rust // in src/serial.rs -pub fn print(args: ::core::fmt::Arguments) { +pub fn _print(args: ::core::fmt::Arguments) { use core::fmt::Write; use x86_64::instructions::interrupts; // new From 0b5e89fbb740ac4f84e4f54fc8f848fb91a664bd Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 18 Nov 2018 15:30:19 +0100 Subject: [PATCH 09/12] Remove all the `extern crate` definitions --- .../posts/02-minimal-rust-kernel/index.md | 6 ---- .../posts/03-vga-text-buffer/index.md | 19 ------------- .../posts/04-unit-testing/index.md | 10 +------ .../posts/05-integration-tests/index.md | 28 +------------------ .../posts/08-hardware-interrupts/index.md | 12 -------- 5 files changed, 2 insertions(+), 73 deletions(-) diff --git a/blog/content/second-edition/posts/02-minimal-rust-kernel/index.md b/blog/content/second-edition/posts/02-minimal-rust-kernel/index.md index 5f7b5f62..f816f306 100644 --- a/blog/content/second-edition/posts/02-minimal-rust-kernel/index.md +++ b/blog/content/second-edition/posts/02-minimal-rust-kernel/index.md @@ -332,12 +332,6 @@ Instead of writing our own bootloader, which is a project on its own, we use the bootloader = "0.3.4" ``` -```rust -// in main.rs - -extern crate bootloader; -``` - Adding the bootloader as dependency is not enough to actually create a bootable disk image. The problem is that we need to combine the bootloader with the kernel after it has been compiled, but cargo has no support for additional build steps after successful compilation (see [this issue][post-build script] for more information). [post-build script]: https://github.com/rust-lang/cargo/issues/545 diff --git a/blog/content/second-edition/posts/03-vga-text-buffer/index.md b/blog/content/second-edition/posts/03-vga-text-buffer/index.md index f7df65c6..0ecb211a 100644 --- a/blog/content/second-edition/posts/03-vga-text-buffer/index.md +++ b/blog/content/second-edition/posts/03-vga-text-buffer/index.md @@ -285,14 +285,6 @@ The `0.2.3` is the [semantic] version number. For more information, see the [Spe [semantic]: http://semver.org/ [Specifying Dependencies]: http://doc.crates.io/specifying-dependencies.html -Now we've declared that our project depends on the `volatile` crate and are able to import it in `src/main.rs`: - -```rust -// in src/main.rs - -extern crate volatile; -``` - Let's use it to make writes to the VGA buffer volatile. We update our `Buffer` type as follows: ```rust @@ -475,12 +467,6 @@ The one-time initialization of statics with non-const functions is a common prob Let's add the `lazy_static` crate to our project: -```rust -// in src/main.rs - -extern crate lazy_static; -``` - ```toml # in Cargo.toml @@ -532,11 +518,6 @@ To use a spinning mutex, we can add the [spin crate] as a dependency: spin = "0.4.9" ``` -```rust -// in src/main.rs -extern crate spin; -``` - Then we can use the spinning Mutex to add safe [interior mutability] to our static `WRITER`: ```rust diff --git a/blog/content/second-edition/posts/04-unit-testing/index.md b/blog/content/second-edition/posts/04-unit-testing/index.md index a98a2848..ae8962e2 100644 --- a/blog/content/second-edition/posts/04-unit-testing/index.md +++ b/blog/content/second-edition/posts/04-unit-testing/index.md @@ -244,18 +244,10 @@ To use that crate, we add the following to our `Cargo.toml`: array-init = "0.0.3" ``` -Note that we're using the [`dev-dependencies`] table instead of the `dependencies` table, because we only need the crate for `cargo test` and not for a normal build. Consequently, we also add a `#[cfg(test)]` attribute to the `extern crate` declaration in `main.rs`: +Note that we're using the [`dev-dependencies`] table instead of the `dependencies` table, because we only need the crate for `cargo test` and not for a normal build. [`dev-dependencies`]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#development-dependencies - -```rust -// in main.rs - -#[cfg(test)] -extern crate array_init; -``` - Now we can fix our `construct_buffer` function: ```rust diff --git a/blog/content/second-edition/posts/05-integration-tests/index.md b/blog/content/second-edition/posts/05-integration-tests/index.md index 2e38b415..56351524 100644 --- a/blog/content/second-edition/posts/05-integration-tests/index.md +++ b/blog/content/second-edition/posts/05-integration-tests/index.md @@ -66,12 +66,6 @@ We will use the [`uart_16550`] crate to initialize the UART and send data over t uart_16550 = "0.1.0" ``` -```rust -// in src/main.rs - -extern crate uart_16550; -``` - The `uart_16550` crate contains a `SerialPort` struct that represents the UART registers, but we still need to construct an instance of it ourselves. For that we create a new `serial` module with the following content: ```rust @@ -210,8 +204,6 @@ x86_64 = "0.2.8" ```rust // in src/main.rs -extern crate x86_64; - pub unsafe fn exit_qemu() { use x86_64::instructions::port::Port; @@ -376,16 +368,6 @@ Cargo supports hybrid projects that are both a library and a binary. We only nee #![cfg_attr(not(test), no_std)] // don't link the Rust standard library -extern crate bootloader; -extern crate spin; -extern crate volatile; -extern crate lazy_static; -extern crate uart_16550; -extern crate x86_64; - -#[cfg(test)] -extern crate array_init; - // NEW: We need to add `pub` here to make them accessible from the outside pub mod vga_buffer; pub mod serial; @@ -405,9 +387,6 @@ pub unsafe fn exit_qemu() { #![cfg_attr(not(test), no_main)] // disable all Rust-level entry points #![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))] -// NEW: Add the library as dependency (same crate name as executable) -extern crate blog_os; - use core::panic::PanicInfo; use blog_os::println; @@ -430,7 +409,7 @@ fn panic(info: &PanicInfo) -> ! { } ``` -So we move everything except `_start` and `panic` to `lib.rs`, make the `vga_buffer` and `serial` modules public, and add an `extern crate` definition to our `main.rs`. Everything should work exactly as before, including `bootimage run` and `cargo test`. +So we move everything except `_start` and `panic` to `lib.rs` and make the `vga_buffer` and `serial` modules public. Everything should work exactly as before, including `bootimage run` and `cargo test`. ### Test Basic Boot @@ -443,9 +422,6 @@ We are finally able to create our first integration test executable. We start si #![cfg_attr(not(test), no_main)] // disable all Rust-level entry points #![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))] -// add the library as dependency (same crate name as executable) -extern crate blog_os; - use core::panic::PanicInfo; use blog_os::{exit_qemu, serial_println}; @@ -507,8 +483,6 @@ To test that our panic handler is really invoked on a panic, we create a `test-p #![cfg_attr(not(test), no_main)] #![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))] -extern crate blog_os; - use core::panic::PanicInfo; use blog_os::{exit_qemu, serial_println}; diff --git a/blog/content/second-edition/posts/08-hardware-interrupts/index.md b/blog/content/second-edition/posts/08-hardware-interrupts/index.md index 58175f31..5d4a71e8 100644 --- a/blog/content/second-edition/posts/08-hardware-interrupts/index.md +++ b/blog/content/second-edition/posts/08-hardware-interrupts/index.md @@ -85,12 +85,6 @@ To add the crate as dependency, we add the following to our project: pic8259_simple = "0.1.1" ``` -```rust -// in src/lib.rs - -extern crate pic8259_simple; -``` - The main abstraction provided by the crate is the [`ChainedPics`] struct that represents the primary/secondary PIC layout we saw above. It is designed to be used in the following way: [`ChainedPics`]: https://docs.rs/pic8259_simple/0.1.1/pic8259_simple/struct.ChainedPics.html @@ -543,12 +537,6 @@ Translating the other keys works in the same way. Fortunately there is a crate n pc-keyboard = "0.3.1" ``` -```rust -// in src/lib.rs - -extern crate pc_keyboard; -``` - Now we can use this crate to rewrite our `keyboard_interrupt_handler`: ```rust From 8e292cc5930eb66907f0866cecd34cae5df6982e Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 19 Nov 2018 09:44:02 +0100 Subject: [PATCH 10/12] Fix typo: invokation -> invocation --- blog/content/second-edition/posts/03-vga-text-buffer/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blog/content/second-edition/posts/03-vga-text-buffer/index.md b/blog/content/second-edition/posts/03-vga-text-buffer/index.md index 0ecb211a..865344ee 100644 --- a/blog/content/second-edition/posts/03-vga-text-buffer/index.md +++ b/blog/content/second-edition/posts/03-vga-text-buffer/index.md @@ -565,7 +565,7 @@ macro_rules! println { } ``` -Macros are defined through one or more rules, which are similar to `match` arms. The `println` macro has two rules: The first rule for is invocations without arguments (e.g `println!()`), which is expanded to `print!("\n)` and thus just prints a newline. the second rule is for invocations with parameters such as `println!("Hello")` or `println!("Number: {}", 4)`. It is also expanded to an invokation of the `print!` macro, passing all arguments and an additional newline `\n` at the end. +Macros are defined through one or more rules, which are similar to `match` arms. The `println` macro has two rules: The first rule for is invocations without arguments (e.g `println!()`), which is expanded to `print!("\n)` and thus just prints a newline. the second rule is for invocations with parameters such as `println!("Hello")` or `println!("Number: {}", 4)`. It is also expanded to an invocation of the `print!` macro, passing all arguments and an additional newline `\n` at the end. The `#[macro_export]` attribute makes the available to the whole crate (not just the module it is defined) and external crates. It also places the macro at the crate root, which means that we have to import the macro through `use std::println` instead of `std::macros::println`. From dbfb73232558eff570f53dc93b0f50456d899132 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 19 Nov 2018 09:45:27 +0100 Subject: [PATCH 11/12] Fix remaining uses of {vga_buffer, serial}::print --- blog/content/second-edition/posts/05-integration-tests/index.md | 2 +- .../second-edition/posts/08-hardware-interrupts/index.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/blog/content/second-edition/posts/05-integration-tests/index.md b/blog/content/second-edition/posts/05-integration-tests/index.md index 56351524..ce0f37b6 100644 --- a/blog/content/second-edition/posts/05-integration-tests/index.md +++ b/blog/content/second-edition/posts/05-integration-tests/index.md @@ -106,7 +106,7 @@ pub fn _print(args: ::core::fmt::Arguments) { #[macro_export] macro_rules! serial_print { ($($arg:tt)*) => { - $crate::serial::print(format_args!($($arg)*)); + $crate::serial::_print(format_args!($($arg)*)); }; } diff --git a/blog/content/second-edition/posts/08-hardware-interrupts/index.md b/blog/content/second-edition/posts/08-hardware-interrupts/index.md index 5d4a71e8..b8faf455 100644 --- a/blog/content/second-edition/posts/08-hardware-interrupts/index.md +++ b/blog/content/second-edition/posts/08-hardware-interrupts/index.md @@ -238,7 +238,7 @@ The hardware timer that we use is called the _Progammable Interval Timer_ or PIT We now have a form of concurrency in our kernel: The timer interrupts occur asynchronously, so they can interrupt our `_start` function at any time. Fortunately Rust's ownership system prevents many types of concurrency related bugs at compile time. One notable exception are deadlocks. Deadlocks occur if a thread tries to aquire a lock that will never become free. Thus the thread hangs indefinitely. -We can already provoke a deadlock in our kernel. Remember, our `println` macro calls the `vga_buffer::print` function, which [locks a global `WRITER`][vga spinlock] using a spinlock: +We can already provoke a deadlock in our kernel. Remember, our `println` macro calls the `vga_buffer::_print` function, which [locks a global `WRITER`][vga spinlock] using a spinlock: [vga spinlock]: ./second-edition/posts/03-vga-text-buffer/index.md#spinlocks From ad7c11c0a3b41913cb90118711b970165d5d12ec Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 19 Nov 2018 10:14:47 +0100 Subject: [PATCH 12/12] Add #[doc(hidden)] to {vga_buffer, serial}::_print functions --- .../second-edition/posts/03-vga-text-buffer/index.md | 9 +++++++-- .../second-edition/posts/05-integration-tests/index.md | 1 + .../second-edition/posts/08-hardware-interrupts/index.md | 3 +++ src/serial.rs | 1 + src/vga_buffer.rs | 1 + 5 files changed, 13 insertions(+), 2 deletions(-) diff --git a/blog/content/second-edition/posts/03-vga-text-buffer/index.md b/blog/content/second-edition/posts/03-vga-text-buffer/index.md index 865344ee..429041b3 100644 --- a/blog/content/second-edition/posts/03-vga-text-buffer/index.md +++ b/blog/content/second-edition/posts/03-vga-text-buffer/index.md @@ -605,17 +605,22 @@ macro_rules! println { ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); } +#[doc(hidden)] pub fn _print(args: fmt::Arguments) { use core::fmt::Write; WRITER.lock().write_fmt(args).unwrap(); } ``` +One thing that we changed from the original `println` definition is that we prefixed the invocations of the `print!` macro with `$crate` too. This ensures that we don't need to have to import the `print!` macro too if we only want to use `println`. + +Like in the standard library, we add the `#[macro_export]` attribute to both macros to make them available everywhere in our crate. Note that this places the macros in the root namespace of the crate, so importing them via `use crate::vga_buffer::println` does not work. Instead, we have to do `use crate::println`. + The `_print` function locks our static `WRITER` and calls the `write_fmt` method on it. This method is from the `Write` trait, we need to import that trait. The additional `unwrap()` at the end panics if printing isn't successful. But since we always return `Ok` in `write_str`, that should not happen. -We also add the `#[macro_export]` attribute to both macros to make them available everywhere in our crate. Note that this places the macros in the root namespace of the crate, so importing them via `use crate::vga_buffer::println` does not work. Instead, we have to do `use crate::println`. +Since the macros need to be able to call `_print` from outside of the module, the function needs to be public. However, since we consider this a private implementation detail, we add the [`doc(hidden)` attribute] to hide it from the generated documentation. -One thing that we changed from the original definitions is that we prefixed the invocations of the `print!` macro with `$crate` too. This ensures that we don't need to have to import the `print!` macro too if we only want to use `println`. +[`doc(hidden)` attribute]: https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#dochidden ### Hello World using `println` Now we can use `println` in our `_start` function: diff --git a/blog/content/second-edition/posts/05-integration-tests/index.md b/blog/content/second-edition/posts/05-integration-tests/index.md index ce0f37b6..89a750c6 100644 --- a/blog/content/second-edition/posts/05-integration-tests/index.md +++ b/blog/content/second-edition/posts/05-integration-tests/index.md @@ -97,6 +97,7 @@ Like with the [VGA text buffer][vga lazy-static], we use `lazy_static` and a spi To make the serial port easily usable, we add `serial_print!` and `serial_println!` macros: ```rust +#[doc(hidden)] pub fn _print(args: ::core::fmt::Arguments) { use core::fmt::Write; SERIAL1.lock().write_fmt(args).expect("Printing to serial failed"); diff --git a/blog/content/second-edition/posts/08-hardware-interrupts/index.md b/blog/content/second-edition/posts/08-hardware-interrupts/index.md index b8faf455..d60dadad 100644 --- a/blog/content/second-edition/posts/08-hardware-interrupts/index.md +++ b/blog/content/second-edition/posts/08-hardware-interrupts/index.md @@ -247,6 +247,7 @@ We can already provoke a deadlock in our kernel. Remember, our `println` macro c […] +#[doc(hidden)] pub fn _print(args: fmt::Arguments) { use core::fmt::Write; WRITER.lock().write_fmt(args).unwrap(); @@ -302,6 +303,7 @@ To avoid this deadlock, we can disable interrupts as long as the `Mutex` is lock /// Prints the given formatted string to the VGA text buffer /// through the global `WRITER` instance. +#[doc(hidden)] pub fn _print(args: fmt::Arguments) { use core::fmt::Write; use x86_64::instructions::interrupts; // new @@ -322,6 +324,7 @@ We can apply the same change to our serial printing function to ensure that no d ```rust // in src/serial.rs +#[doc(hidden)] pub fn _print(args: ::core::fmt::Arguments) { use core::fmt::Write; use x86_64::instructions::interrupts; // new diff --git a/src/serial.rs b/src/serial.rs index ccab77ec..2c105f97 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -10,6 +10,7 @@ lazy_static! { }; } +#[doc(hidden)] pub fn _print(args: ::core::fmt::Arguments) { use core::fmt::Write; use x86_64::instructions::interrupts; diff --git a/src/vga_buffer.rs b/src/vga_buffer.rs index 729241c6..a361a49f 100644 --- a/src/vga_buffer.rs +++ b/src/vga_buffer.rs @@ -162,6 +162,7 @@ macro_rules! println { } /// Prints the given formatted string to the VGA text buffer through the global `WRITER` instance. +#[doc(hidden)] pub fn _print(args: fmt::Arguments) { use core::fmt::Write; use x86_64::instructions::interrupts;