Giter Site home page Giter Site logo

rust-osdev / multiboot2 Goto Github PK

View Code? Open in Web Editor NEW
104.0 104.0 53.0 650 KB

The multiboot2 crate helps to parse the Multiboot2 information structure (MBI) and is relevant in kernels, that get booted by a bootloader such as GRUB, for example. multiboot2-header helps you to either build Multiboot2-headers yourself, or to parse Multiboot2 headers in custom bootloader or similar applications.

License: Apache License 2.0

Rust 94.33% Assembly 1.74% Nix 2.68% Shell 1.24%

multiboot2's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

multiboot2's Issues

Multiboot2-header: wrong use of `packed(N)`

Some time ago, I thought that packed(N) is like packed plus align(N), which is not the case. I should remove the packed(N) everywhere and use the default representation. I think that there should be no padding, i can verify this with a unit test. If required, I can add packed/packed(1)

RSDP checksum and ext_checksum are not particularly useful

Summary

A correct calculation of the RSDP checksum requires access to enum members that are private or reserved.

Description

AFAIK the RsdpV1Tag::checksum, RsdpV2Tag::checksum, and RsdpV2Tag::ext_checksum are not particularly useful in their current form. From ACPI 6.3 Section 5.2.5.3 Table 5-27:

The Checksum

This is the checksum of the fields... This includes only the first 20 bytes of this table, bytes 0 to 19, including the checksum field. These bytes must sum to zero.

The Extended Checksum

This is a checksum of the entire table, including both checksum fields.

A correct calculation of either checksum requires access to private members of the RsdpV1Tag and RsdpV2Tag for which there is no access function defined.

Possible Solution

Instead of surfacing these members like RsdpV2Tag::_rsdt_address and RsdpV2Tag::length provide the RsdpV1Tag::verify_checksum, RsdpV2Tag::verify_checksum, and RsdpV2Tag::verify_ext_checksum functions that perform the calculation.

Implement Send on BootInformation?

BootInformation doesn't implement Send, because it contains a raw pointer. From the nomicon:

A type is Send if it is safe to send it to another thread.

The pointer inside the BootInformation is const, so we cannot modify it across threads anyways. Is there any downside to doing an unsafe impl Send for BootInformation {} ? My goal is to store it inside a spin::Once<T>, and that requires it to be Send.

Access to non-available memory areas

The MemoryMapTag struct in memory_map.rs provides a memory_areas function that allows iterating over memory areas, but it skips memory areas that are any type other than available. It would be helpful to get an iterator over all memory areas. I suggest adding a function all_memory_areas to MemoryMapTag which would provide an iterator that doesn't skip any areas.

Fix CI: CI does not use the promised Rust version

By experiments, I found out that the current Github CI does not use the specified Rust version. I could verify this by using "1.30.0", an old version that definitely does not work.

Minimal Reproducible Example

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        rust:
          - 1.30.0 # MSVR
    steps:
      - uses: actions/checkout@v2
      - run: cargo --version
      - uses: actions-rs/cargo@v1
        with:
          command: version

Both commands return 1.60.0 which is the current latest stable version.

multiboot2-header: various improvements

  • rename std module to alloc
  • multiboot2_header::load should return not only a byte slice (seen in #136)
  • also, the assertion could be transformed into a variant of the error enum

The implementation can/should be more generic, and play nicer with lifetimes

The comment in the function tries to explain safety with "The multiboot specification defines the module str as valid utf-8, therefore this function produces defined behavior". Such trust in constraint assumed by specification is a horribly wrong thing to do.

It might not matter much when the data is used by kernel to boot, as corrupted data there automatically break all safety, but that makes the implicit assumption that the crate will never, ever, ever, be used to access multiboot data in other contexts, e.g. a memory dump for debugging.

No multiboot module support

Hey, thanks for this crate.
I'm willing to implement this, but if you don't want it in your repository I can do it in my own fork.

multiboot2: Missing Tags [call for contribution]

I just found out that the multiboot2 crate does not contain nice high level types for all possible multiboot2 tags yet. Here is a list of missing types. If you want to contribute, please start here:

  • EndTag (0x0) (currently hard coded.. could be done nicer) (#138)
  • BasicMeminfo (0x4) (#137)
  • Bootdev (0x5)
  • Apm (0xa)
  • Smbios (0xc) (#137)
  • Network (0x10)

Docs? Code examples?

Im having some trouble understanding how to use it and the source code in phil-opp's blog is out of date

multiboot2-header is not `no_std`

The docs claim the library is no_std in the second sentence of the front page: https://github.com/rust-osdev/multiboot2/blob/main/multiboot2-header/README.md?plain=1#L7

However the code references std in multiple places:

Multiboot2HeaderBuilder::build(): https://github.com/rust-osdev/multiboot2/blob/main/multiboot2-header/src/header/builder.rs#L129

std is used in the beginning of lib.rs: https://github.com/rust-osdev/multiboot2/blob/main/multiboot2-header/src/lib.rs#L74

When I got build errors using this I was hoping there would be a std feature I could turn off, but it doesn't exist :(

The docs should probably be updated

i386 kernels cannot use MemoryMapTag safely

I'm currently writing an i386 kernel, and am using multiboot2 to parse the multiboot2 information. Unfortunately, the MemoryMapTag returns the start_address/end_address as a usize, whereas they are stored as u64. https://github.com/rust-osdev/multiboot2-elf64/blob/master/src/memory_map.rs#L33.

For 32-bit kernels, this means that if a PC has more than 4GB of available RAM, we'll get some completely wrong values, and end up thinking that a range from 0x000000-0xXXXXXX is available, wrecking havoc. Those should be sent to return u64s (which I did locally in my kernel), but that would be a breaking change.

ELF section name

I'm willing to do this but I'll need to read some more on ELF64 first. I think that it is useful for debugging purposes to see which sections are what easily. If I understand correctly how getting strings works the string table address is in the ELF multiboot tag, and the indices to section names reside in the sections themselves. I suppose that there can be a pub fn name(&self, *const StrTable) or something similar but I'm not sure if this is the best option. Feel free to suggest.

lib.rs needs #![feature(no_std)]

Recent rust nightlies (at least as far back as 2015-11-19) require 'no_std' to be enabled as a feature before it can be used. Lacking that, the #![no_std] line results in a compile error:

/home/vagrant/.multirust/toolchains/nightly-2015-11-19/cargo/git/checkouts/multiboot2-elf64-151908bccf532495/master/src/lib.rs:1:1: 1:11 error: no_std is experimental (see issue #27701)
/home/vagrant/.multirust/toolchains/nightly-2015-11-19/cargo/git/checkouts/multiboot2-elf64-151908bccf532495/master/src/lib.rs:1 #![no_std]
                                                                                                                                 ^~~~~~~~~~
/home/vagrant/.multirust/toolchains/nightly-2015-11-19/cargo/git/checkouts/multiboot2-elf64-151908bccf532495/master/src/lib.rs:1:1: 1:11 help: add #![feature(no_std)] to the crate attributes to enable
error: aborting due to previous error
Could not compile `multiboot2`.

repr(C) is necessary

Currently, several structures use #[repr(packed)]. This works merely by coincidence, since packed is just a modifier, not a standalone representation (equivalent to #[repr(rust, packed)]). Correct annotation should be #[repr(C, packed)].

Even better would be to fix the padding by fixing structure fields, since packed exists to allow misaligned field accesses (i.e. special code in place of every load and store), which is totally unnecessary for aligned multiboot data.

Other tags?

I see that both get_tag and tags are not public, is there any reason for this? I'd love to be able to query what other tags may be in the header.

Inconsistency in Kernel memory address

Hey! This is likely an error on my part but after digging for a while I think I should report it just in case.

I have been following your os blog. I set up my own mechanism to determine the start and end of the Kernel but it doesn't line up with the values from this crate. I set up my linker.ld script as follows:

ENTRY(_start)

SECTIONS
{
    . = 1M;

    KERNEL_START = .;

        /* All sections */

    KERNEL_END = .;
}

And then in my rust kernel:

enum Void {}

extern "C" {
    static KERNEL_START: Void;
    static KERNEL_END: Void;
}

pub fn kernel_memory_start() -> u64 {
    &KERNEL_START as *const _ as u64
}

pub fn kernel_memory_end() -> u64 {
    &KERNEL_END as *const _ as u64
}

The values I get:

Kernel start: 1048576
Kernel end: 1114112

The values from this crate:

Kernel start: 1048576
Kernel end: 1211936

From my limited understanding there is no reason why these should not be the same? I'm also unsure as to why my kernel doesn't start at 1M but I'm overlooking that for now :)

Maintainance of this crate

Hi! I haven't had the time to maintain this crate. There are a few outstanding issues and pull requests in this repository and various forks have spawned. So I thought about how we could improve the situation.

The most important question is: Is anybody willing to maintain this crate?

If not, I would propose to give all interested people push access, but require pull requests and reviews for all changes. There is also a travis check in place that ensures that the crate always builds and that the tests always pass. This way, the crate could be developed further without a dedicated maintainer.

We could also move this crate to a Github organization if there's interest, and give more people push access to crates.io.

What do you think?

cc:

@ahmedcharles
@robert-w-gries
@aeleos
@Columbus240
@le-jzr
@4e554c4c

multiboot2_header: README.md

MULTIBOOT2_HDR is an address of array, not an array.

#[used]
#[no_mangle]
#[link_section = ".text.multiboot2_header"]
static MULTIBOOT2_HDR: &[u8; 64] = include_bytes!("mb2_hdr_dump.bin");

It should be below.

macrorules! mb2_hdr_bytes
{
  ()=>
  {
    include_bytes!("mb2_hdr_dump.bin")
  };
}

#[used]
#[no_mangle]
#[link_section = ".text.multiboot2_header"]
static MULTIBOOT2_HDR: [u8; 64] = *include_bytes!("mb2_hdr_dump.bin");
// or static MULTIBOOT2_HDR: [u8; mb2_hdr_bytes!().len()] = *mb2_hdr_bytes!();

Warning safe_packed_borrows will become a hard error

Warning

While restarting a garbage Travis build, I noticed the following warnings:

warning: #[derive] can't be used on a non-Copy #[repr(packed)] struct (error E0133)
 --> src/header.rs:1:10
  |
1 | #[derive(Debug)]
  |          ^^^^^
  |
  = note: #[warn(safe_packed_borrows)] on by default
  = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
  = note: for more information, see issue #46043 <https://github.com/rust-lang/rust/issues/46043>

warning: borrow of packed field requires unsafe function or block (error E0133)
 --> src/elf_sections.rs:9:5
  |
9 |     assert_eq!(9, tag.typ);
  |     ^^^^^^^^^^^^^^^^^^^^^^^
  |
  = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
  = note: for more information, see issue #46043 <https://github.com/rust-lang/rust/issues/46043>
  = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

There are around 20 more #[derive(Debug)] warnings that I snipped for brevity.

#[repr(packed)]

The warning is related to our usage of #[repr(packed)] in the crate. There is undefined behavior associated with derefencing unaligned structs, which rust-lang/rust#46043 will prevent with a compiler error.

How to Fix

From the rust issue:

The most common ways to fix this warnings are:

  1. remove the #[repr(packed)] attribute if it is not actually needed. A few crates had unnecessary #[repr(packed)] annotations - for example, tendril.
  2. copy the fields to a local first. When accessing a field of a packed struct directly without using a borrow, the compiler will make sure the access is done correctly even when the field is unaligned. The then-aligned local can then be freely used. For example:
let x = Foo { start: 0, data: Unaligned(1) };
let temp = x.data.0;
println!("{}", temp); // works
// or, to the same effect, using an `{x}` block:
println!("{}", {x.data.0}); // works
use(y);

One annoying case where this problem can appear is if a packed struct has builtin derives

If your struct already derives Copy and has no generics, the compiler will generate code that copies the fields to locals and will therefore work. Otherwise, you'll have to write the derive implementations manually.

Feedback

Before submitting a PR, I'd like feedback on the following:

  1. Do our impacted structs need to use#[repr(packed)]? It seems that #[repr(packed)] should be a last resort instead of a feature liberally used.
  2. Can we implement Copy for our affected structs? Or do we need to manually implement Debug for those structs?

miri: improve current situation

In #128, I added basic miri support. Due to the following issues, miri can't embrace all it's funcitionality:

  • Each test that uses the cast_tag (transitively) fails, as miri can't guarantee that the memory is valid, unfortunately
  • when something like struct_as_bytes() ... cast is done, there are alignment issues as the returned Vec is of course only aligned to a one byte boundary... so far, in tests this never was a problem, luckily. At least on x86.

License?

What license is this under? If we wanted to include this in intermezzOS, it'd need to be open source, and by default, this is "all rights reserved".

I'm assuming since it's a tiny module you just forgot about this ๐Ÿ˜„

Multiboot 1 support

Multiboot2 is a fine spec in theory, but in practice there are only a few bootloaders which support it. More importantly, qemu has support for directly loading Multiboot 1 kernels, but not mb2. Ideally I want the ability to parse mb1 structures and build them via the builder. Maybe even build hybrid mb1/mb2 binaries for maximum compatibility with bootloaders. I'm willing to do the work myself, but for that I have the following questions:
Do you want multiboot1 support in this crate or should it be a separate crate?
If yes, should it be behind a feature flag? Default on or off?
Should I put the mb1 files in the src directory prefixed with mb1_ or in a seperate mb1 folder/module?

ElfSectionIter never returns the last section

Looks like there's an off-by-one error in the ElfSectionIter. Due to this, the last ELF section is never returned by the iterator.

The remaining_sections is initialized to number_of_sections - 1. In next(), when remaining_sections == 0, it returns none. So in a hypothetical kernel where number_of_sections was 1, next() would never return anything.

Adapt to multiboot2 v2.0

About a year ago a new version of the multiboot2 specification was created. It is backwards compatible to v1.6 which is currently used in this repository. The new version provides features useful for EFI based systems. For example among others a copy of the ACPI RSDP structure is available in the boot information.

Necessary changes to this crate would be adding the necessary API to access the new tags in the multiboot2 boot information and adapt to all other differences between the two versions of the specification.

The specification is available (seemingly since ever) in the GRUB git repository https://git.savannah.gnu.org/git/grub.git on the multiboot2 branch.
In it, the following commands have to be executed: ./autogen.sh; ./configure; make pdf. This will generate a pdf file doc/multiboot.pdf which contains the multiboot2 specification.
I don't know exactly what dependencies are required to generate this file, but among them are certainly gnulib for autogen.sh and texinfo because the spec is written in texinfo format.

If you don't want to go through the generation process and trust me (that I didn't mess with the file), you can download the file attached below. I don't guarantee that the information in the file I have uploaded is correct. (If in doubt, generate the file yourself.) I built it off commit d81931d0e5830467b778f20b7eca761240d337f7 made on 2017-03-10.
multiboot.pdf

P.S.: I just read the discussion about issue #22. If this repo is copied and/or redone, it would still be great to provide support for the newer multiboot2 features. If this repo isn't forked, my idea still stands.

Returned strings include null terminator?

If you e.g. writeln!(serial, "{:?}", info.boot_loader_name_tag().unwrap().name()), you get "GRUB 2.02~beta3\u{0}" instead of just "GRUB 2.02~beta3". Is this incorrect behavior? (It's unexpected, at least).

Evaluate Interface for Custom Tags

I've seen people using custom Multiboot2 tags to transfer data from a custom bootloader to their kernel. We should evaluate if and how we want or can support this.

[Bug] Assertion failed when parsing EFI Multiboot memory region header

Known versions

The bug can be reproduced with version 0.19.0 and 0.20.0. While 0.18.1 is functioning.

Bug behavior

Panicked with

panicked at .cargo/registry/src/index.crates.io-6f17d22bba15001f/multiboot2-0.19.0/src/memory_map.rs:335:9:
assertion `left == right` failed
  left: 24
 right: 0

at

impl TagTrait for EFIMemoryMapTag {
const ID: TagType = TagType::EfiMmap;
fn dst_size(base_tag: &Tag) -> usize {
assert!(base_tag.size as usize >= EFI_METADATA_SIZE);
let size = base_tag.size as usize - EFI_METADATA_SIZE;
assert_eq!(size % mem::size_of::<EFIMemoryDesc>(), 0);
size / mem::size_of::<EFIMemoryDesc>()
}
}

Context

I used let mb2_info = BootInformation::load(addr) to load the header, and try to debug print it with "{:#?}", mb2_info.

The following information is printed:

Multiboot2BootInformation {
    start_address: 589824,
    end_address: 599200,
    total_size: 9376,
    basic_memory_info: Some(
        BasicMemoryInfoTag {
            typ: BasicMeminfo,
            size: 16,
            memory_lower: 640,
            memory_upper: 7168,
        },
    ),
    boot_loader_name: Some(
        BootLoaderNameTag {
            typ: BootLoaderName,
            size: 18,
            name: Ok(
                "GRUB 2.12",
            ),
        },
    ),
    command_line: Some(
        CommandLineTag {
            typ: Cmdline,
            size: 104,
            cmdline: Ok(
                "SHELL=/bin/sh LOGNAME=root HOME=/ USER=root PATH=/bin:/benchmark init=/usr/bin/busybox -- sh -l",
            ),
        },
    ),
    efi_bs_not_exited: None,

And it stops printing with a panic here.

Environment

  • OVMF built with EDK2 version edk2-stable202402 ;
  • QEMU built from source version 8.2.1.

I don't have time build a minimal reproduction demo. But one can do it under our kernel project https://github.com/asterinas/asterinas. There's a development image for the exact environment https://hub.docker.com/r/asterinas/asterinas.

Plans to fix it

We may use 0.18.1 for our project currently. I am happy to provide assistance for anyone taking it. I can also take it but I need guidance.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.