Refactor build system, boot and added Linux Boot facilities

This commit is contained in:
Zhang Junyang 2023-08-09 11:40:00 +08:00 committed by Tate, Hongliang Tian
parent 6ff4601482
commit 7d5295ab25
24 changed files with 1268 additions and 422 deletions

View File

@ -16,6 +16,12 @@ jobs:
- uses: actions/checkout@v3
- name: Syscall Test
id: syscall_test
run: RUSTFLAGS="-C opt-level=1" make run AUTO_SYSCALL_TEST=1 ENABLE_KVM=0
- name: Syscall Test (Multiboot2)
id: syscall_test_mb2
run: RUSTFLAGS="-C opt-level=1" make run AUTO_SYSCALL_TEST=1 ENABLE_KVM=0 SKIP_GRUB_MENU=1 BOOT_METHOD=grub-multiboot2
- name: Syscall Test (Linux Boot Protocol)
id: syscall_test_lbp
run: RUSTFLAGS="-C opt-level=1" make run AUTO_SYSCALL_TEST=1 ENABLE_KVM=0 SKIP_GRUB_MENU=1 BOOT_METHOD=grub-linux
# TODO: include the integration tests for Multiboot and MicroVM, which are not ready yet.

180
Cargo.lock generated
View File

@ -49,30 +49,29 @@ dependencies = [
[[package]]
name = "anstream"
version = "0.3.2"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is-terminal",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.1"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd"
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
[[package]]
name = "anstyle-parse"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140"
dependencies = [
"utf8parse",
]
@ -88,9 +87,9 @@ dependencies = [
[[package]]
name = "anstyle-wincon"
version = "1.0.2"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c"
checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
dependencies = [
"anstyle",
"windows-sys",
@ -98,9 +97,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.72"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]]
name = "arrayvec"
@ -180,15 +179,6 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
[[package]]
name = "cc"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01"
dependencies = [
"libc",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
@ -197,20 +187,19 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.3.21"
version = "4.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd"
checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956"
dependencies = [
"clap_builder",
"clap_derive",
"once_cell",
]
[[package]]
name = "clap_builder"
version = "4.3.21"
version = "4.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa"
checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45"
dependencies = [
"anstream",
"anstyle",
@ -220,9 +209,9 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "4.3.12"
version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050"
checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873"
dependencies = [
"heck",
"proc-macro2",
@ -232,9 +221,9 @@ dependencies = [
[[package]]
name = "clap_lex"
version = "0.5.0"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
[[package]]
name = "colorchoice"
@ -420,27 +409,6 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f"
dependencies = [
"errno-dragonfly",
"libc",
"windows-sys",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "ext-trait"
version = "1.0.1"
@ -510,6 +478,12 @@ dependencies = [
"syn 2.0.28",
]
[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "hash32"
version = "0.2.1"
@ -553,12 +527,6 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
[[package]]
name = "ident_case"
version = "1.0.1"
@ -620,17 +588,6 @@ dependencies = [
"ghost",
]
[[package]]
name = "is-terminal"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
dependencies = [
"hermit-abi",
"rustix",
"windows-sys",
]
[[package]]
name = "itertools"
version = "0.10.5"
@ -671,6 +628,8 @@ version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"glob",
"rand",
]
[[package]]
@ -921,12 +880,6 @@ dependencies = [
"rle-decode-fast",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
[[package]]
name = "lock_api"
version = "0.4.10"
@ -1065,6 +1018,12 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a69ee997a6282f8462abf1e0d8c38c965e968799e912b3bed8c9e8a28c2f9f"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
@ -1133,6 +1092,36 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "raw-cpuid"
version = "10.7.0"
@ -1175,19 +1164,6 @@ dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "0.38.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f"
dependencies = [
"bitflags 2.3.3",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "rustversion"
version = "1.0.14"
@ -1497,9 +1473,9 @@ dependencies = [
[[package]]
name = "windows-targets"
version = "0.48.1"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
@ -1512,45 +1488,45 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "winnow"

View File

@ -22,6 +22,7 @@ jinux-framebuffer = { path = "services/comps/framebuffer" }
members = [
"build",
"framework/jinux-frame",
"framework/libs/align_ext",
"services/comps/virtio",
"services/comps/input",
"services/comps/block",

View File

@ -1,10 +1,13 @@
# Make arguments and their defaults
AUTO_SYSCALL_TEST ?= 0
BOOT_METHOD ?= grub-multiboot2
BUILD_SYSCALL_TEST ?= 0
EMULATE_IOMMU ?= 0
RUN_MICROVM ?= 0
ENABLE_KVM ?= 1
GDB_CLIENT ?= 0
GDB_SERVER ?= 0
INTEL_TDX ?= 0
SKIP_GRUB_MENU ?= 0
# End of Make arguments
KERNEL_CMDLINE := SHELL="/bin/sh" LOGNAME="root" HOME="/" USER="root" PATH="/bin" init=/usr/bin/busybox -- sh -l
@ -16,30 +19,48 @@ CARGO_KBUILD_ARGS :=
CARGO_KRUN_ARGS := -- '$(KERNEL_CMDLINE)'
ifeq ($(INTEL_TDX), 1)
CARGO_KBUILD_ARGS += --features intel_tdx
CARGO_KRUN_ARGS += --features intel_tdx
ifeq ($(AUTO_SYSCALL_TEST), 1)
BUILD_SYSCALL_TEST := 1
endif
CARGO_KRUN_ARGS += --boot-method="$(BOOT_METHOD)"
ifeq ($(EMULATE_IOMMU), 1)
CARGO_KRUN_ARGS += --emulate-iommu
endif
ifeq ($(ENABLE_KVM), 1)
CARGO_KRUN_ARGS += --enable-kvm
endif
ifeq ($(EMULATE_IOMMU), 1)
CARGO_KRUN_ARGS += --emulate-iommu
ifeq ($(GDB_SERVER), 1)
ENABLE_KVM := 0
CARGO_KRUN_ARGS += --halt-for-gdb
endif
ifeq ($(RUN_MICROVM), 1)
CARGO_KRUN_ARGS += --run-microvm
ifeq ($(GDB_CLIENT), 1)
CARGO_KRUN_ARGS += --run-gdb-client
endif
ifeq ($(AUTO_SYSCALL_TEST), 1)
BUILD_SYSCALL_TEST := 1
ifeq ($(INTEL_TDX), 1)
CARGO_KBUILD_ARGS += --features intel_tdx
CARGO_KRUN_ARGS += --features intel_tdx
endif
ifeq ($(SKIP_GRUB_MENU), 1)
CARGO_KRUN_ARGS += --skip-grub-menu
endif
# Pass make variables to all subdirectory makes
export
export JINUX_BOOT_PROTOCOL=$(BOOT_PROTOCOL)
# GNU toolchain variables
export AS := as
export CC := gcc
export OBJCOPY := objcopy
.PHONY: all setup build tools run test docs check clean
all: build

View File

@ -1,10 +1,27 @@
use std::error::Error;
use std::{error::Error, path::PathBuf};
fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
let linker_script_path = "framework/jinux-frame/src/arch/x86/boot/linker.ld";
println!("cargo:rerun-if-changed={}", linker_script_path);
println!("cargo:rustc-link-arg=-T{}", linker_script_path);
let linker_script_path = get_source_dir()
.join("framework")
.join("jinux-frame")
.join("src")
.join("arch")
.join("x86")
.join("boot")
.join("linker.ld");
println!(
"cargo:rerun-if-changed={}",
linker_script_path.to_str().unwrap()
);
println!(
"cargo:rustc-link-arg=-T{}",
linker_script_path.to_str().unwrap()
);
println!("cargo:rerun-if-env-changed=CARGO_PKG_NAME");
Ok(())
}
fn get_source_dir() -> PathBuf {
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
PathBuf::from(manifest_dir)
}

View File

@ -7,3 +7,5 @@ edition = "2021"
[dependencies]
anyhow = "1.0.32"
clap = { version = "4.3.19", features = ["derive"] }
glob = "0.3.1"
rand = "0.8.5"

View File

@ -2,11 +2,11 @@
# AUTOMATICALLY GENERATED FILE, DO NOT EDIT IF YOU KNOW WHAT YOU ARE DOING
set timeout_style=menu
set timeout=1
set timeout_style=#GRUB_TIMEOUT_STYLE#
set timeout=#GRUB_TIMEOUT#
menuentry 'jinux' {
multiboot2 /boot/jinux #KERNEL_COMMAND_LINE#
module2 --nounzip /boot/initramfs.cpio.gz
#GRUB_CMD_KERNEL# /boot/jinux #KERNEL_COMMAND_LINE#
#GRUB_CMD_INITRAMFS# /boot/initramfs.cpio.gz
boot
}

View File

@ -1,9 +1,11 @@
use std::{
fs,
fs::{self, File},
io::{Read, Write},
path::{Path, PathBuf},
};
use glob::glob;
pub const MACHINE_ARGS: &[&str] = &["-machine", "q35,kernel-irqchip=split"];
pub const NOIOMMU_DEVICE_ARGS: &[&str] = &[
@ -28,12 +30,23 @@ pub const IOMMU_DEVICE_ARGS: &[&str] = &[
"ioh3420,id=pcie.0,chassis=1",
];
pub fn create_bootdev_image(path: PathBuf, kcmdline: &str) -> String {
#[derive(Debug, Clone, Copy)]
pub enum GrubBootProtocol {
Multiboot,
Multiboot2,
Linux,
}
pub fn create_bootdev_image(
path: PathBuf,
grub_cfg: String,
protocol: GrubBootProtocol,
) -> PathBuf {
let dir = path.parent().unwrap();
let name = path.file_name().unwrap().to_str().unwrap().to_string();
let iso_path = dir.join(name + ".iso").to_str().unwrap().to_string();
// Clean up the image directory
// Clean up the image directory.
if Path::new("target/iso_root").exists() {
fs::remove_dir_all("target/iso_root").unwrap();
}
@ -41,19 +54,38 @@ pub fn create_bootdev_image(path: PathBuf, kcmdline: &str) -> String {
// Copy the needed files into an ISO image.
fs::create_dir_all("target/iso_root/boot/grub").unwrap();
fs::copy(path.as_os_str(), "target/iso_root/boot/jinux").unwrap();
generate_grub_cfg(
"build/grub/grub.cfg.template",
"target/iso_root/boot/grub/grub.cfg",
kcmdline,
);
fs::copy(
"regression/build/initramfs.cpio.gz",
"target/iso_root/boot/initramfs.cpio.gz",
)
.unwrap();
// Make the boot device .iso image
// Find the setup header in the build script output directory.
let out_dir = glob("target/x86_64-custom/debug/build/jinux-frame-*").unwrap();
let header_bin = Path::new(out_dir.into_iter().next().unwrap().unwrap().as_path())
.join("out/linux_header.bin");
// Deliver the kernel image to the boot directory.
match protocol {
GrubBootProtocol::Linux => {
// Make the `zimage`-compatible kernel image and place it in the boot directory.
make_zimage(
&Path::new("target/iso_root/boot/jinux"),
&path.as_path(),
&header_bin.as_path(),
)
.unwrap();
}
GrubBootProtocol::Multiboot | GrubBootProtocol::Multiboot2 => {
// Copy the kernel image into the boot directory.
fs::copy(&path, "target/iso_root/boot/jinux").unwrap();
}
}
// Write the grub.cfg file
fs::write("target/iso_root/boot/grub/grub.cfg", grub_cfg).unwrap();
// Make the boot device .iso image.
let status = std::process::Command::new("grub-mkrescue")
.arg("-o")
.arg(&iso_path)
@ -65,24 +97,85 @@ pub fn create_bootdev_image(path: PathBuf, kcmdline: &str) -> String {
panic!("Failed to create boot iso image.")
}
iso_path
iso_path.into()
}
fn generate_grub_cfg(template_filename: &str, target_filename: &str, kcmdline: &str) {
pub fn generate_grub_cfg(
template_filename: &str,
kcmdline: &str,
skip_grub_menu: bool,
protocol: GrubBootProtocol,
) -> String {
let mut buffer = String::new();
// Read the contents of the file
// Read the contents of the file.
fs::File::open(template_filename)
.unwrap()
.read_to_string(&mut buffer)
.unwrap();
// Replace all occurrences of "#KERNEL_COMMAND_LINE#" with the desired value
let replaced_content = buffer.replace("#KERNEL_COMMAND_LINE#", kcmdline);
// Delete the first two lines that notes the file is a template file.
let buffer = buffer.lines().skip(2).collect::<Vec<&str>>().join("\n");
// Set the timout style and timeout.
let buffer = buffer
.replace(
"#GRUB_TIMEOUT_STYLE#",
if skip_grub_menu { "hidden" } else { "menu" },
)
.replace("#GRUB_TIMEOUT#", if skip_grub_menu { "0" } else { "1" });
// Replace all occurrences of "#KERNEL_COMMAND_LINE#" with the desired value.
let buffer = buffer.replace("#KERNEL_COMMAND_LINE#", kcmdline);
// Replace the grub commands according to the protocol selected.
let buffer = match protocol {
GrubBootProtocol::Multiboot => buffer
.replace("#GRUB_CMD_KERNEL#", "multiboot")
.replace("#GRUB_CMD_INITRAMFS#", "module --nounzip"),
GrubBootProtocol::Multiboot2 => buffer
.replace("#GRUB_CMD_KERNEL#", "multiboot2")
.replace("#GRUB_CMD_INITRAMFS#", "module2 --nounzip"),
GrubBootProtocol::Linux => buffer
.replace("#GRUB_CMD_KERNEL#", "linux")
.replace("#GRUB_CMD_INITRAMFS#", "initrd"),
};
// Write the modified content back to the file
fs::File::create(target_filename)
.unwrap()
.write_all(replaced_content.as_bytes())
.unwrap();
buffer
}
fn fill_header_field(header: &mut [u8], offset: usize, value: &[u8]) {
let size = value.len();
assert_eq!(
&header[offset..offset + size],
vec![0xABu8; size].as_slice()
);
header[offset..offset + size].copy_from_slice(value);
}
fn make_zimage(path: &Path, kernel_path: &Path, header_path: &Path) -> std::io::Result<()> {
let mut header = Vec::new();
File::open(header_path)?.read_to_end(&mut header)?;
// Pad the header to let the payload starts with 8-byte alignment.
header.resize((header.len() + 7) & !7, 0x00);
let mut kernel = Vec::new();
File::open(kernel_path)?.read_to_end(&mut kernel)?;
let header_len = header.len();
let kernel_len = kernel.len();
fill_header_field(
&mut header,
0x248, /* payload_offset */
&(header_len as u32).to_le_bytes(),
);
fill_header_field(
&mut header,
0x24C, /* payload_length */
&(kernel_len as u32).to_le_bytes(),
);
let mut kernel_image = File::create(path)?;
kernel_image.write_all(&header)?;
kernel_image.write_all(&kernel)?;
Ok(())
}

View File

@ -21,7 +21,7 @@ pub const DEVICE_ARGS: &[&str] = &[
"virtio-net-device,netdev=net01",
];
pub fn create_bootdev_image(path: PathBuf) -> String {
pub fn create_bootdev_image(path: PathBuf) -> PathBuf {
let dir = path.parent().unwrap();
let name = path.file_name().unwrap().to_str().unwrap().to_string();
let elf_path = dir.join(name.clone()).to_str().unwrap().to_string();
@ -70,5 +70,5 @@ pub fn create_bootdev_image(path: PathBuf) -> String {
file.write_all(&bytes).unwrap();
file.flush().unwrap();
strip_elf_path
strip_elf_path.into()
}

View File

@ -1,9 +1,12 @@
//! jinux-build is the Jinux runner script to ease the pain of running
//! and testing Jinux inside a QEMU VM, which should be called as the
//! jinux-runner is the Jinux runner script to ease the pain of running
//! and testing Jinux inside a QEMU VM. It should be built and run as the
//! cargo runner: https://doc.rust-lang.org/cargo/reference/config.html
//!
//! The runner generates the the filesystem image and the containing
//! boot device image. Then it invokes QEMU to boot Jinux.
//! The runner will generate the filesystem image for starting Jinux. If
//! we should use the runner in the default mode, which invokes QEMU with
//! a GRUB boot device image, the runner would be responsible for generating
//! the and the boot device image. It also supports directly boot the
//! kernel image without GRUB using the QEMU microvm mode.
//!
pub mod machine;
@ -31,6 +34,14 @@ struct Args {
kcmdline: String,
// Optional arguments.
/// Boot method. Can be one of the following items:
/// - `grub-multiboot`,
/// - `grub-multiboot2`,
/// - `grub-linux`,
/// - `microvm-multiboot`.
#[arg(long, default_value = "grub-multiboot2")]
boot_method: String,
/// Enable KVM when running QEMU.
#[arg(long, default_value_t = false)]
enable_kvm: bool,
@ -39,9 +50,17 @@ struct Args {
#[arg(long, default_value_t = false)]
emulate_iommu: bool,
/// Run Jinux as microvm mode.
/// Run QEMU as a GDB server.
#[arg(long, default_value_t = false)]
run_microvm: bool,
halt_for_gdb: bool,
/// Boot without displaying the GRUB menu.
#[arg(long, default_value_t = false)]
skip_grub_menu: bool,
/// Run a GDB client instead of running the kernel.
#[arg(long, default_value_t = false)]
run_gdb_client: bool,
}
pub const COMMON_ARGS: &[&str] = &[
@ -59,57 +78,114 @@ pub const COMMON_ARGS: &[&str] = &[
"none",
"-device",
"isa-debug-exit,iobase=0xf4,iosize=0x04",
"-netdev",
"user,id=net01,hostfwd=tcp::30133-:22,hostfwd=tcp::31088-:8080",
"-object",
"filter-dump,id=filter0,netdev=net01,file=virtio-net.pcap",
];
pub fn random_hostfwd_netdev_arg() -> String {
let start = 32768u16;
let end = 61000u16;
let port1 = rand::random::<u16>() % (end - 1 - start) + start;
let port2 = rand::random::<u16>() % (end - port1) + port1;
format!(
"user,id=net01,hostfwd=tcp::{}-:22,hostfwd=tcp::{}-:8080",
port1, port2
)
}
pub const GDB_ARGS: &[&str] = &[
"-chardev",
"socket,path=/tmp/jinux-gdb-socket,server=on,wait=off,id=gdb0",
"-gdb",
"chardev:gdb0",
"-S",
];
fn main() {
let args = Args::parse();
if args.run_gdb_client {
let mut gdb_cmd = Command::new("gdb");
// Adding the debug symbols from the kernel image.
// Alternatively, use "file /usr/lib/grub/i386-pc/boot.image"
// to load symbols from GRUB.
gdb_cmd
.arg("-ex")
.arg(format!("file {}", args.path.display()));
// Set the architecture, otherwise GDB will complain about.
gdb_cmd.arg("-ex").arg("set arch i386:x86-64:intel");
// Connect to the GDB server.
gdb_cmd
.arg("-ex")
.arg("target remote /tmp/jinux-gdb-socket");
println!("running:{:#?}", gdb_cmd);
gdb_cmd.status().unwrap();
return;
}
let mut qemu_cmd = Command::new("qemu-system-x86_64");
let mut qemu_args = COMMON_ARGS.to_vec();
qemu_cmd.args(COMMON_ARGS);
qemu_cmd.arg("-netdev");
qemu_cmd.arg(random_hostfwd_netdev_arg().as_str());
if args.halt_for_gdb {
if args.enable_kvm {
println!("Runner: Can't enable KVM when running QEMU as a GDB server. Abort.");
return;
}
qemu_cmd.args(GDB_ARGS);
}
if args.enable_kvm {
qemu_args.push("-enable-kvm");
qemu_cmd.arg("-enable-kvm");
}
// Specify machine type
if args.run_microvm {
qemu_args.extend(microvm::MACHINE_ARGS.to_vec().iter());
if args.boot_method == "microvm-multiboot" {
qemu_cmd.args(microvm::MACHINE_ARGS);
} else {
qemu_args.extend(default::MACHINE_ARGS.to_vec().iter());
qemu_cmd.args(default::MACHINE_ARGS);
}
// Add device arguments
if args.run_microvm {
qemu_args.extend(microvm::DEVICE_ARGS.to_vec().iter())
if args.boot_method == "microvm-multiboot" {
qemu_cmd.args(microvm::DEVICE_ARGS);
} else if args.emulate_iommu {
qemu_args.extend(default::IOMMU_DEVICE_ARGS.to_vec().iter());
qemu_cmd.args(default::IOMMU_DEVICE_ARGS);
} else {
qemu_args.extend(default::NOIOMMU_DEVICE_ARGS.to_vec().iter());
qemu_cmd.args(default::NOIOMMU_DEVICE_ARGS);
}
let fs_image = create_fs_image(args.path.as_path());
qemu_args.push("-drive");
qemu_args.push(fs_image.as_str());
qemu_cmd.arg("-drive");
qemu_cmd.arg(fs_image);
if args.run_microvm {
if args.boot_method == "microvm-multiboot" {
let image = microvm::create_bootdev_image(args.path);
qemu_cmd.arg("-kernel");
qemu_cmd.arg(image.as_str());
qemu_cmd.arg(image.as_os_str());
qemu_cmd.arg("-append");
qemu_cmd.arg(&args.kcmdline);
qemu_cmd.arg("-initrd");
qemu_cmd.arg("regression/build/initramfs.cpio.gz");
} else {
let bootdev_image = default::create_bootdev_image(args.path, &args.kcmdline);
let boot_protocol = match args.boot_method.as_str() {
"grub-multiboot" => default::GrubBootProtocol::Multiboot,
"grub-multiboot2" => default::GrubBootProtocol::Multiboot2,
"grub-linux" => default::GrubBootProtocol::Linux,
_ => panic!("Unknown boot method: {}", args.boot_method),
};
let grub_cfg = default::generate_grub_cfg(
"build/grub/grub.cfg.template",
&args.kcmdline,
args.skip_grub_menu,
boot_protocol,
);
let bootdev_image = default::create_bootdev_image(args.path, grub_cfg, boot_protocol);
qemu_cmd.arg("-cdrom");
qemu_cmd.arg(bootdev_image.as_str());
qemu_cmd.arg(bootdev_image.as_os_str());
}
qemu_cmd.args(qemu_args);
println!("running:{:#?}", qemu_cmd);
let exit_status = qemu_cmd.status().unwrap();

View File

@ -0,0 +1,63 @@
use std::{error::Error, io::Write, path::PathBuf};
fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
build_linux_setup_header()?;
Ok(())
}
fn get_source_dir() -> PathBuf {
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
PathBuf::from(manifest_dir)
}
fn get_header_out_dir() -> PathBuf {
PathBuf::from(std::env::var("OUT_DIR").unwrap())
}
fn build_linux_setup_header() -> Result<(), Box<dyn Error + Send + Sync>> {
// Compile the header to raw binary.
let linux_boot_header_asm_path = get_source_dir()
.join("src")
.join("arch")
.join("x86")
.join("boot")
.join("linux_boot")
.join("header.S");
println!(
"cargo:rerun-if-changed={}",
linux_boot_header_asm_path.to_str().unwrap()
);
let linux_boot_header_elf_path = get_header_out_dir().join("linux_header.o");
let gas = std::env::var("AS").unwrap();
let mut cmd = std::process::Command::new(gas);
cmd.arg(linux_boot_header_asm_path);
cmd.arg("-o")
.arg(linux_boot_header_elf_path.to_str().unwrap());
let output = cmd.output()?;
if !output.status.success() {
std::io::stdout().write_all(&output.stdout).unwrap();
std::io::stderr().write_all(&output.stderr).unwrap();
panic!(
"Failed to compile linux boot header:\n\tcommand `{:?}`\n\treturned {}",
cmd, output.status
);
}
// Strip the elf header to get the raw header.
let linux_boot_header_bin_path = get_header_out_dir().join("linux_header.bin");
let objcopy = std::env::var("OBJCOPY").unwrap();
let mut cmd = std::process::Command::new(objcopy);
cmd.arg("-O").arg("binary");
cmd.arg("-j").arg(".boot_compatibility_bin");
cmd.arg(linux_boot_header_elf_path.to_str().unwrap());
cmd.arg(linux_boot_header_bin_path.to_str().unwrap());
let output = cmd.output()?;
if !output.status.success() {
std::io::stdout().write_all(&output.stdout).unwrap();
std::io::stderr().write_all(&output.stderr).unwrap();
panic!(
"Failed to strip linux boot header:\n\tcommand `{:?}`\n\treturned {}",
cmd, output.status
);
}
Ok(())
}

View File

@ -4,68 +4,78 @@
.section ".boot", "awx"
.code32
// The entry point of our ELF target.
.global __boot
__boot:
jmp initial_boot_setup
ENTRYTYPE_MULTIBOOT = 1
ENTRYTYPE_MULTIBOOT2 = 2
ENTRYTYPE_LINUX_32 = 3
// This is the GNU Multiboot 2 header.
// Reference: https://www.gnu.org/software/grub/manual/multiboot2/html_node/Index.html//Index
// Macros for cleaner code in the header fields.
MB2_MAGIC = 0xE85250D6
MB2_ARCHITECTURE = 0 // 32-bit (protected) mode of i386
MB2_HEADERLEN = header_end - header_start
MB2_CHECKSUM = -(MB2_MAGIC + MB2_ARCHITECTURE + MB2_HEADERLEN)
MB_MAGIC = 0x1BADB002
MB_FLAGS = 0
MB_CHECKSUM = -(MB_MAGIC + MB_FLAGS)
header_start:
.align 8
.long MB2_MAGIC
.long MB2_ARCHITECTURE
.long MB2_HEADERLEN
.long MB2_CHECKSUM
// Tag: information request
.align 8
info_request:
.short 1
.short 0 // Required
.long info_request_end - info_request
.long 6 // Memory map request
.long 15 // ACPI (new) request
info_request_end:
// Tag: header end
.align 8
.short 0 // type: tags end
.short 0 // flags
.long 8 // size
header_end:
multiboot_header:
.align 8
.long MB_MAGIC
.long MB_FLAGS
.long MB_CHECKSUM
initial_boot_setup:
// The Linux 32-bit Boot Protocol entry point.
// Must be located at 0x100000, ABI immutable!
.code32
.org 0x000
.global __linux32_boot
__linux32_boot:
cli
cld
// Set the kernel call stack.
mov esp, offset boot_stack_top
// Save the multiboot magic and 64-bit physical address of multiboot info onto the stack.
push 0 // Upper 32-bits.
push eax
push 0 // Upper 32-bits.
push ebx
push 0 // upper 32-bits
push esi // boot_params ptr
push 0 // upper 32-bits
push ENTRYTYPE_LINUX_32
jmp initial_boot_setup
// The Linux 64-bit Boot Protocol entry point.
// Must be located at 0x100200, ABI immutable!
.code64
.org 0x200
.global __linux64_boot_tag
__linux64_boot_tag:
lea rax, [rip + __linux64_boot] // jump into Rust code
call rax
jmp halt // unreachable here
// The multiboot entry point.
.code32
.global __multiboot_boot
__multiboot_boot:
cli
cld
// Set the kernel call stack.
mov esp, offset boot_stack_top
push 0 // Upper 32-bits.
push eax // multiboot magic ptr
push 0 // Upper 32-bits.
push ebx // multiboot info ptr
push 0 // Upper 32-bits.
push ENTRYTYPE_MULTIBOOT
jmp initial_boot_setup
// The multiboot2 entry point.
.code32
.global __multiboot2_boot
__multiboot2_boot:
cli
cld
// Set the kernel call stack.
mov esp, offset boot_stack_top
push 0 // Upper 32-bits.
push eax // multiboot magic ptr
push 0 // Upper 32-bits.
push ebx // multiboot info ptr
push 0 // Upper 32-bits.
push ENTRYTYPE_MULTIBOOT2
jmp initial_boot_setup
initial_boot_setup:
// Prepare for far return. We use a far return as a fence after setting GDT.
mov eax, 24
push eax
@ -284,17 +294,38 @@ long_mode:
cld
rep stosb
// Call the corresponding Rust entrypoint according to the boot entrypoint
pop rax
cmp rax, ENTRYTYPE_MULTIBOOT2
je entry_type_multiboot2
cmp rax, ENTRYTYPE_LINUX_32
je entry_type_pvh_elf
// Unreachable!
jmp halt
entry_type_pvh_elf:
pop rdi // boot_params ptr
// Clear the frame pointer to stop backtracing here.
xor rbp, rbp
.extern __linux64_boot
lea rax, [rip + __linux64_boot] // jump into Rust code
call rax
jmp halt
entry_type_multiboot2:
pop rsi // the address of multiboot info
pop rdi // multiboot magic
// Clear the frame pointer to stop backtracing here.
xor rbp, rbp
.extern __boot_entry
lea rax, [rip + __boot_entry] // jump into Rust code
.extern __multiboot2_entry
lea rax, [rip + __multiboot2_entry] // jump into Rust code
call rax
jmp halt
// In case boot() returns.
halt:
cli
hlt

View File

@ -1,4 +1,4 @@
ENTRY(__boot)
ENTRY(__linux64_boot)
OUTPUT_ARCH(i386:x86-64)
OUTPUT_FORMAT(elf64-x86-64)
@ -10,7 +10,9 @@ SECTIONS
. = KERNEL_LMA;
__kernel_start = .;
.multiboot_header : { KEEP(*(.multiboot_header)) }
.multiboot2_header : { KEEP(*(.multiboot2_header)) }
.boot : { KEEP(*(.boot)) }
. += KERNEL_VMA;
@ -47,5 +49,8 @@ SECTIONS
__eh_frame_end = .;
}
# The notes section are used to mark the PVH boot entry point, useful for QEMU and Xen
.notes : { *(.notes) }
__kernel_end = . - KERNEL_VMA;
}

View File

@ -0,0 +1,281 @@
//! The Linux Boot Protocol boot_params module.
//!
//! The bootloader will deliver the address of the `BootParams` struct
//! as the argument of the kernel entrypoint. So we must define a Linux
//! ABI compatible struct in Rust, despite that most of the fields are
//! currently not needed by Jinux.
//!
#[repr(C, packed)]
pub(super) struct ScreenInfo {
pub(super) orig_x: u8, /* 0x00 */
pub(super) orig_y: u8, /* 0x01 */
pub(super) ext_mem_k: u16, /* 0x02 */
pub(super) orig_video_page: u16, /* 0x04 */
pub(super) orig_video_mode: u8, /* 0x06 */
pub(super) orig_video_cols: u8, /* 0x07 */
pub(super) flags: u8, /* 0x08 */
pub(super) unused2: u8, /* 0x09 */
pub(super) orig_video_ega_bx: u16, /* 0x0a */
pub(super) unused3: u16, /* 0x0c */
pub(super) orig_video_lines: u8, /* 0x0e */
pub(super) orig_video_is_vga: u8, /* 0x0f */
pub(super) orig_video_points: u16, /* 0x10 */
/* VESA graphic mode -- linear frame buffer */
pub(super) lfb_width: u16, /* 0x12 */
pub(super) lfb_height: u16, /* 0x14 */
pub(super) lfb_depth: u16, /* 0x16 */
pub(super) lfb_base: u32, /* 0x18 */
pub(super) lfb_size: u32, /* 0x1c */
pub(super) cl_magic: u16,
pub(super) cl_offset: u16, /* 0x20 */
pub(super) lfb_linelength: u16, /* 0x24 */
pub(super) red_size: u8, /* 0x26 */
pub(super) red_pos: u8, /* 0x27 */
pub(super) green_size: u8, /* 0x28 */
pub(super) green_pos: u8, /* 0x29 */
pub(super) blue_size: u8, /* 0x2a */
pub(super) blue_pos: u8, /* 0x2b */
pub(super) rsvd_size: u8, /* 0x2c */
pub(super) rsvd_pos: u8, /* 0x2d */
pub(super) vesapm_seg: u16, /* 0x2e */
pub(super) vesapm_off: u16, /* 0x30 */
pub(super) pages: u16, /* 0x32 */
pub(super) vesa_attributes: u16, /* 0x34 */
pub(super) capabilities: u32, /* 0x36 */
pub(super) ext_lfb_base: u32, /* 0x3a */
pub(super) _reserved: [u8; 2], /* 0x3e */
}
#[repr(C, packed)]
pub(super) struct ApmBiosInfo {
pub(super) version: u16,
pub(super) cseg: u16,
pub(super) offset: u32,
pub(super) cseg_16: u16,
pub(super) dseg: u16,
pub(super) flags: u16,
pub(super) cseg_len: u16,
pub(super) cseg_16_len: u16,
pub(super) dseg_len: u16,
}
#[repr(C, packed)]
pub(super) struct IstInfo {
pub(super) signature: u32,
pub(super) command: u32,
pub(super) event: u32,
pub(super) perf_level: u32,
}
#[repr(C, packed)]
pub(super) struct SysDescTable {
pub(super) length: u16,
pub(super) table: [u8; 14],
}
#[repr(C, packed)]
pub(super) struct OlpcOfwHeader {
pub(super) ofw_magic: u32, /* OFW signature */
pub(super) ofw_version: u32,
pub(super) cif_handler: u32, /* callback into OFW */
pub(super) irq_desc_table: u32,
}
#[repr(C)]
pub(super) struct EdidInfo {
pub(super) dummy: [u8; 128],
}
#[repr(C)]
pub(super) struct EfiInfo {
pub(super) efi_loader_signature: u32,
pub(super) efi_systab: u32,
pub(super) efi_memdesc_size: u32,
pub(super) efi_memdesc_version: u32,
pub(super) efi_memmap: u32,
pub(super) efi_memmap_size: u32,
pub(super) efi_systab_hi: u32,
pub(super) efi_memmap_hi: u32,
}
/// Magic stored in SetupHeader.boot_flag.
pub(super) const LINUX_BOOT_FLAG_MAGIC: u16 = 0xAA55;
/// Magic stored in SetupHeader.header.
pub(super) const LINUX_BOOT_HEADER_MAGIC: u32 = 0x53726448;
/// Linux Boot Protocol Header.
///
/// Originally defined in the linux source tree:
/// `linux/arch/x86/include/uapi/asm/bootparam.h`
#[repr(C, packed)]
pub(super) struct SetupHeader {
pub(super) setup_sects: u8,
pub(super) root_flags: u16,
pub(super) syssize: u32,
pub(super) ram_size: u16,
pub(super) vid_mode: u16,
pub(super) root_dev: u16,
pub(super) boot_flag: u16,
pub(super) jump: u16,
pub(super) header: u32,
pub(super) version: u16,
pub(super) realmode_swtch: u32,
pub(super) start_sys_seg: u16,
pub(super) kernel_version: u16,
pub(super) type_of_loader: u8,
pub(super) loadflags: u8,
pub(super) setup_move_size: u16,
pub(super) code32_start: u32,
pub(super) ramdisk_image: u32,
pub(super) ramdisk_size: u32,
pub(super) bootsect_kludge: u32,
pub(super) heap_end_ptr: u16,
pub(super) ext_loader_ver: u8,
pub(super) ext_loader_type: u8,
pub(super) cmd_line_ptr: u32,
pub(super) initrd_addr_max: u32,
pub(super) kernel_alignment: u32,
pub(super) relocatable_kernel: u8,
pub(super) min_alignment: u8,
pub(super) xloadflags: u16,
pub(super) cmdline_size: u32,
pub(super) hardware_subarch: u32,
pub(super) hardware_subarch_data: u64,
pub(super) payload_offset: u32,
pub(super) payload_length: u32,
pub(super) setup_data: u64,
pub(super) pref_address: u64,
pub(super) init_size: u32,
pub(super) handover_offset: u32,
pub(super) kernel_info_offset: u32,
}
/// The E820 types known to the kernel.
///
/// Originally defined in the linux source tree:
/// `linux/arch/x86/include/asm/e820/types.h`
#[derive(Copy, Clone)]
#[repr(u32)]
pub(super) enum E820Type {
Ram = 1,
Reserved = 2,
Acpi = 3,
Nvs = 4,
Unusable = 5,
Pmem = 7,
/*
* This is a non-standardized way to represent ADR or
* NVDIMM regions that persist over a reboot.
*
* The kernel will ignore their special capabilities
* unless the CONFIG_X86_PMEM_LEGACY=y option is set.
*
* ( Note that older platforms also used 6 for the same
* type of memory, but newer versions switched to 12 as
* 6 was assigned differently. Some time they will learn... )
*/
Pram = 12,
/*
* Special-purpose memory is indicated to the system via the
* EFI_MEMORY_SP attribute. Define an e820 translation of this
* memory type for the purpose of reserving this range and
* marking it with the IORES_DESC_SOFT_RESERVED designation.
*/
SoftReserved = 0xefffffff,
/*
* Reserved RAM used by the kernel itself if
* CONFIG_INTEL_TXT=y is enabled, memory of this type
* will be included in the S3 integrity calculation
* and so should not include any memory that the BIOS
* might alter over the S3 transition:
*/
ReservedKern = 128,
}
#[repr(C, packed)]
pub(super) struct BootE820Entry {
pub(super) addr: u64,
pub(super) size: u64,
pub(super) typ: E820Type,
}
const E820_MAX_ENTRIES_ZEROPAGE: usize = 128;
#[repr(C, packed)]
pub(super) struct EddDeviceParams {
// TODO: We currently have no plans to support the edd device,
// and we need unnamed fields (Rust RFC 2102) to implement this
// FFI neatly. So we put a dummy implementation here conforming
// to the BootParams struct ABI.
pub(super) _dummy: [u8; (0xeec - 0xd00) / 6 - 8],
}
#[repr(C, packed)]
pub(super) struct EddInfo {
pub(super) device: u8,
pub(super) version: u8,
pub(super) interface_support: u16,
pub(super) legacy_max_cylinder: u16,
pub(super) legacy_max_head: u8,
pub(super) legacy_sectors_per_track: u8,
pub(super) params: EddDeviceParams,
}
const EDD_MBR_SIG_MAX: usize = 16;
const EDDMAXNR: usize = 6;
/// Linux 32/64-bit Boot Protocol parameter struct.
///
/// Originally defined in the linux source tree:
/// `linux/arch/x86/include/uapi/asm/bootparam.h`
#[repr(C, packed)]
pub(super) struct BootParams {
pub(super) screen_info: ScreenInfo, /* 0x000 */
pub(super) apm_bios_info: ApmBiosInfo, /* 0x040 */
pub(super) _pad2: [u8; 4], /* 0x054 */
pub(super) tboot_addr: u64, /* 0x058 */
pub(super) ist_info: IstInfo, /* 0x060 */
pub(super) acpi_rsdp_addr: u64, /* 0x070 */
pub(super) _pad3: [u8; 8], /* 0x078 */
pub(super) hd0_info: [u8; 16], /* obsolete! 0x080 */
pub(super) hd1_info: [u8; 16], /* obsolete! 0x090 */
pub(super) sys_desc_table: SysDescTable, /* obsolete! 0x0a0 */
pub(super) olpc_ofw_header: OlpcOfwHeader, /* 0x0b0 */
pub(super) ext_ramdisk_image: u32, /* 0x0c0 */
pub(super) ext_ramdisk_size: u32, /* 0x0c4 */
pub(super) ext_cmd_line_ptr: u32, /* 0x0c8 */
pub(super) _pad4: [u8; 112], /* 0x0cc */
pub(super) cc_blob_address: u32, /* 0x13c */
pub(super) edid_info: EdidInfo, /* 0x140 */
pub(super) efi_info: EfiInfo, /* 0x1c0 */
pub(super) alt_mem_k: u32, /* 0x1e0 */
pub(super) scratch: u32, /* Scratch field! 0x1e4 */
pub(super) e820_entries: u8, /* 0x1e8 */
pub(super) eddbuf_entries: u8, /* 0x1e9 */
pub(super) edd_mbr_sig_buf_entries: u8, /* 0x1ea */
pub(super) kbd_status: u8, /* 0x1eb */
pub(super) secure_boot: u8, /* 0x1ec */
pub(super) _pad5: [u8; 2], /* 0x1ed */
/*
* The sentinel is set to a nonzero value (0xff) in header.S.
*
* A bootloader is supposed to only take setup_header and put
* it into a clean boot_params buffer. If it turns out that
* it is clumsy or too generous with the buffer, it most
* probably will pick up the sentinel variable too. The fact
* that this variable then is still 0xff will let kernel
* know that some variables in boot_params are invalid and
* kernel should zero out certain portions of boot_params.
*/
pub(super) sentinel: u8, /* 0x1ef */
pub(super) _pad6: [u8; 1], /* 0x1f0 */
pub(super) hdr: SetupHeader, /* setup header 0x1f1 */
pub(super) _pad7: [u8; 0x290 - 0x1f1 - core::mem::size_of::<SetupHeader>()],
pub(super) edd_mbr_sig_buffer: [u32; EDD_MBR_SIG_MAX], /* 0x290 */
pub(super) e820_table: [BootE820Entry; E820_MAX_ENTRIES_ZEROPAGE], /* 0x2d0 */
pub(super) _pad8: [u8; 48], /* 0xcd0 */
pub(super) eddbuf: [EddInfo; EDDMAXNR], /* 0xd00 */
pub(super) _pad9: [u8; 276], /* 0xeec */
}

View File

@ -0,0 +1,115 @@
// The compatibility file for the Linux x86 Boot Protocol.
// See https://www.kernel.org/doc/html/v5.6/x86/boot.html for
// more information on the Linux x86 Boot Protocol.
.intel_syntax noprefix
// The section name is used by the build script to strip and make
// the binary file.
.section ".boot_compatibility_bin", "awx"
// The Linux x86 Boot Protocol header.
//
// Some of the fields filled with a 0xab* values should be filled
// by the runner, which is the only tool after building and can
// access the info of the payload.
// Jinux will use only a few of these fields, and some of them
// are filled by the loader and will be read by Jinux.
.code16
sentinel: .byte 0xff, 0xff
.org 0x01f1
hdr:
setup_sects: .byte 0
root_flags: .word 1
syssize: .long 0
ram_size: .word 0
vid_mode: .word 0xfffd
root_dev: .word 0
boot_flag: .word 0xAA55
jump: .byte 0xeb
.byte start_of_setup-jump
magic: .ascii "HdrS"
.word 0x020f
realmode_swtch: .word 0, 0
start_sys_seg: .word 0
.word 0
type_of_loader: .byte 0
loadflags: .byte (1 << 0)
setup_move_size: .word 0x8000
code32_start: .long 0x100000
ramdisk_image: .long 0
ramdisk_size: .long 0
bootsect_kludge: .long 0
heap_end_ptr: .word 65535
ext_loader_ver: .byte 0
ext_loader_type: .byte 0
cmd_line_ptr: .long 0
initrd_addr_max: .long 0x7fffffff
kernel_alignment: .long 0x1000000
relocatable_kernel: .byte 0
min_alignment: .byte 0x10
xloadflags: .word 0 # none of the flags supported
cmdline_size: .long 4096-1
hardware_subarch: .long 0
hardware_subarch_data: .quad 0
payload_offset: .long 0xabababab # at 0x248/4, to be filled by the runner
payload_length: .long 0xabababab # at 0x24c/4, to be filled by the runner
setup_data: .quad 0
pref_address: .quad 0
init_size: .long 0
handover_offset: .long 0
kernel_info_offset: .long 0
// End of header.
// Temporary real mode GDTR/GDT entries.
.align 16
real_gdtr:
.word gdt_end - gdt - 1
.quad gdt
.align 16
gdt:
.quad 0x0000000000000000 # 0: null descriptor
.quad 0x00cf8a000000ffff # 8: 32-bit system segment (4k sys ex rw)
.quad 0x00cf9a000000ffff # 16: 32-bit code/data segment (4k sys ex rw)
gdt_end:
// 16-bit setup code starts here.
.code16
start_of_setup:
// Enter 32-bit protected mode without paging.
// Disable interrupts.
cli
// Enable a20 gate.
in al, 0x92
or al, 2
out 0x92, al
// Load GDT.
lgdt [real_gdtr]
mov eax, cr0
or eax, 1
mov cr0, eax
// Go to protected mode.
jmp start_of_setup32
// 32-bit setup code starts here.
.code32
start_of_setup32:
// print to screen a debug message using out port 0x3f8.
mov dx, 0x3f8
mov al, 'H'
out dx, al
mov al, 'e'
out dx, al
mov al, 'l'
out dx, al
mov al, 'l'
out dx, al
mov al, 'o'
out dx, al

View File

@ -0,0 +1,151 @@
//! The Linux 64-bit Boot Protocol supporting module.
//!
mod boot_params;
use boot_params::E820Type;
use crate::boot::{
kcmdline::KCmdlineArg,
memory_region::{MemoryRegion, MemoryRegionType},
BootloaderAcpiArg, BootloaderFramebufferArg,
};
use crate::{config::PHYS_OFFSET, vm::paddr_to_vaddr};
use alloc::{borrow::ToOwned, format, string::String, vec::Vec};
use core::ffi::CStr;
use spin::Once;
static BOOT_PARAMS: Once<boot_params::BootParams> = Once::new();
fn init_bootloader_name(bootloader_name: &'static Once<String>) {
let hdr = &BOOT_PARAMS.get().unwrap().hdr;
// The bootloaders have assigned IDs in Linux, see
// https://www.kernel.org/doc/Documentation/x86/boot.txt
// for details.
let ext_str: String;
let name = match hdr.type_of_loader {
0x0 => "LILO", // (0x00 reserved for pre-2.00 bootloader)
0x1 => "Loadlin",
0x2 => "bootsect-loader", // (0x20, all other values reserved)
0x3 => "Syslinux",
0x4 => "Etherboot/gPXE/iPXE",
0x5 => "ELILO",
0x7 => "GRUB",
0x8 => "U-Boot",
0x9 => "Xen",
0xA => "Gujin",
0xB => "Qemu",
0xC => "Arcturus Networks uCbootloader",
0xD => "kexec-tools",
0xE => {
// Extended
ext_str = format!(
"Extended bootloader {}, version {}",
(hdr.ext_loader_type + 0x10),
(hdr.type_of_loader & 0x0f) + (hdr.ext_loader_ver << 4)
);
&ext_str
}
0xF => "Special", // (0xFF = undefined)
0x10 => "Reserved",
0x11 => "Minimal Linux Bootloader <http://sebastian-plotz.blogspot.de>",
0x12 => "OVMF UEFI virtualization stack",
_ => "Unknown bootloader type!",
}
.to_owned();
bootloader_name.call_once(|| name);
}
fn init_kernel_commandline(kernel_cmdline: &'static Once<KCmdlineArg>) {
let cmdline_c_str: &CStr =
unsafe { CStr::from_ptr(BOOT_PARAMS.get().unwrap().hdr.cmd_line_ptr as *const i8) };
let cmdline_str = cmdline_c_str.to_str().unwrap();
kernel_cmdline.call_once(|| cmdline_str.into());
}
fn init_initramfs(initramfs: &'static Once<&'static [u8]>) {
let hdr = &BOOT_PARAMS.get().unwrap().hdr;
let ptr = hdr.ramdisk_image as usize;
// We must return a slice composed by VA since kernel should read everything in VA.
let base_va = if ptr < PHYS_OFFSET {
paddr_to_vaddr(ptr)
} else {
ptr
};
let length = hdr.ramdisk_size as usize;
initramfs.call_once(|| unsafe { core::slice::from_raw_parts(base_va as *const u8, length) });
}
fn init_acpi_arg(acpi: &'static Once<BootloaderAcpiArg>) {
acpi.call_once(|| {
BootloaderAcpiArg::Rsdp(
BOOT_PARAMS
.get()
.unwrap()
.acpi_rsdp_addr
.try_into()
.unwrap(),
)
});
}
fn init_framebuffer_info(framebuffer_arg: &'static Once<BootloaderFramebufferArg>) {
let screen_info = &BOOT_PARAMS.get().unwrap().screen_info;
framebuffer_arg.call_once(|| BootloaderFramebufferArg {
address: screen_info.lfb_base as usize,
width: screen_info.lfb_width as usize,
height: screen_info.lfb_height as usize,
bpp: screen_info.lfb_depth as usize,
});
}
impl From<E820Type> for MemoryRegionType {
fn from(value: E820Type) -> Self {
match value {
E820Type::Ram => Self::Usable,
E820Type::Reserved => Self::Reserved,
E820Type::Acpi => Self::Reclaimable,
E820Type::Nvs => Self::NonVolatileSleep,
_ => Self::BadMemory,
}
}
}
fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
let boot_params = &BOOT_PARAMS.get().unwrap();
let num_entries = boot_params.e820_entries as usize;
let mut regions = Vec::<MemoryRegion>::new();
for e820_entry in &boot_params.e820_table[0..num_entries] {
regions.push(MemoryRegion::new(
e820_entry.addr as usize,
e820_entry.size as usize,
e820_entry.typ.into(),
));
}
memory_regions.call_once(|| regions);
}
// The entry point of kernel code, which should be defined by the package that
// uses jinux-frame.
extern "Rust" {
fn jinux_main() -> !;
}
/// The entry point of Rust code called by the Linux 64-bit boot compatible bootloader.
/// It is the ELF entrypoint.
#[no_mangle]
unsafe extern "sysv64" fn __linux64_boot(params: boot_params::BootParams) -> ! {
assert_eq!({ params.hdr.boot_flag }, boot_params::LINUX_BOOT_FLAG_MAGIC);
assert_eq!({ params.hdr.header }, boot_params::LINUX_BOOT_HEADER_MAGIC);
BOOT_PARAMS.call_once(|| params);
crate::boot::register_boot_init_callbacks(
init_bootloader_name,
init_kernel_commandline,
init_initramfs,
init_acpi_arg,
init_framebuffer_info,
init_memory_regions,
);
jinux_main();
}

View File

@ -1,65 +1,25 @@
//! The boot module defines the entrypoints of Jinux and the corresponding
//! headers for different bootloaders.
//! The x86 boot module defines the entrypoints of Jinux and
//! the corresponding headers for different x86 boot protocols.
//!
//! We currently support Multiboot2. The support for Linux Boot Protocol is
//! on its way.
//! We directly support
//!
//! - Multiboot
//! - Multiboot2
//! - Linux x86 Boot Protocol
//!
//! without any additional configurations.
//!
//! Jinux diffrentiates the boot protocol by the entry point
//! chosen by the boot loader. In each entry point function,
//! the universal callback registeration method from
//! `crate::boot` will be called. Thus the initialization of
//! boot information is transparent for the upper level kernel.
//!
mod linux_boot;
mod multiboot;
mod multiboot2;
use core::arch::global_asm;
use alloc::{string::String, vec::Vec};
use spin::Once;
use crate::boot::{
kcmdline::KCmdlineArg, memory_region::MemoryRegion, BootloaderAcpiArg, BootloaderFramebufferArg,
};
use self::{
multiboot::{multiboot_entry, MULTIBOOT_ENTRY_MAGIC},
multiboot2::{multiboot2_entry, MULTIBOOT2_ENTRY_MAGIC},
};
/// Initialize the global boot static varaiables in the boot module to allow
/// other modules to get the boot information.
pub fn init_boot_args(
bootloader_name: &'static Once<String>,
kernel_cmdline: &'static Once<KCmdlineArg>,
initramfs: &'static Once<&'static [u8]>,
acpi: &'static Once<BootloaderAcpiArg>,
framebuffer_arg: &'static Once<BootloaderFramebufferArg>,
memory_regions: &'static Once<Vec<MemoryRegion>>,
) {
if multiboot::boot_by_multiboot() {
multiboot::init_boot_args(
bootloader_name,
kernel_cmdline,
initramfs,
acpi,
framebuffer_arg,
memory_regions,
);
} else if multiboot2::boot_by_multiboot2() {
multiboot2::init_boot_args(
bootloader_name,
kernel_cmdline,
initramfs,
acpi,
framebuffer_arg,
memory_regions,
);
}
}
global_asm!(include_str!("boot.S"));
#[no_mangle]
unsafe extern "C" fn __boot_entry(boot_magic: u32, boot_params: u64) -> ! {
match boot_magic {
MULTIBOOT2_ENTRY_MAGIC => multiboot2_entry(boot_magic, boot_params),
MULTIBOOT_ENTRY_MAGIC => multiboot_entry(boot_magic, boot_params),
_ => panic!("Unknown boot magic:{:x?}", boot_magic),
}
}

View File

@ -0,0 +1,17 @@
// This is the GNU Multiboot header.
// Reference: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html
.section ".multiboot_header", "a"
MB_MAGIC = 0x1BADB002
MB_FLAGS = 0
MB_CHECKSUM = -(MB_MAGIC + MB_FLAGS)
.code32
multiboot_header:
.align 8
.long MB_MAGIC
.long MB_FLAGS
.long MB_CHECKSUM
multiboot_entry:
.global __multiboot_boot
jmp __multiboot_boot

View File

@ -1,5 +1,3 @@
use core::mem::swap;
use alloc::{string::String, vec::Vec};
use multiboot2::MemoryAreaType;
use spin::Once;
@ -8,37 +6,19 @@ use crate::{
arch::x86::kernel::acpi::AcpiMemoryHandler,
boot::{
kcmdline::KCmdlineArg,
memory_region::{MemoryRegion, MemoryRegionType},
memory_region::{non_overlapping_regions_from, MemoryRegion, MemoryRegionType},
BootloaderAcpiArg, BootloaderFramebufferArg,
},
config::PHYS_OFFSET,
vm::paddr_to_vaddr,
};
use core::arch::global_asm;
global_asm!(include_str!("header.S"));
pub(super) const MULTIBOOT_ENTRY_MAGIC: u32 = 0x2BADB002;
/// Initialize the global boot static varaiables in the boot module to allow
/// other modules to get the boot information.
pub(super) fn init_boot_args(
bootloader_name: &'static Once<String>,
kernel_cmdline: &'static Once<KCmdlineArg>,
initramfs: &'static Once<&'static [u8]>,
acpi: &'static Once<BootloaderAcpiArg>,
framebuffer_arg: &'static Once<BootloaderFramebufferArg>,
memory_regions: &'static Once<Vec<MemoryRegion>>,
) {
init_bootloader_name(bootloader_name);
init_kernel_commandline(kernel_cmdline);
init_initramfs(initramfs);
init_acpi_arg(acpi);
init_framebuffer_info(framebuffer_arg);
init_memory_regions(memory_regions);
}
pub fn boot_by_multiboot() -> bool {
MB1_INFO.is_completed()
}
fn init_bootloader_name(bootloader_name: &'static Once<String>) {
bootloader_name.call_once(|| {
let mut name = "";
@ -135,12 +115,7 @@ fn init_framebuffer_info(framebuffer_arg: &'static Once<BootloaderFramebufferArg
}
fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
// We should later use regions in `regions_unusable` to truncate all
// regions in `regions_usable`.
// The difference is that regions in `regions_usable` could be used by
// the frame allocator.
let mut regions_usable = Vec::<MemoryRegion>::new();
let mut regions_unusable = Vec::<MemoryRegion>::new();
let mut regions = Vec::<MemoryRegion>::new();
// Add the regions in the multiboot protocol.
let info = MB1_INFO.get().unwrap();
@ -157,14 +132,7 @@ fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
entry.length.try_into().unwrap(),
area_type,
);
match area_type {
MemoryRegionType::Usable | MemoryRegionType::Reclaimable => {
regions_usable.push(region);
}
_ => {
regions_unusable.push(region);
}
}
regions.push(region);
current += entry.size as usize + 4;
}
@ -175,7 +143,7 @@ fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
height: info.framebuffer_table.height as usize,
bpp: info.framebuffer_table.bpp as usize,
};
regions_unusable.push(MemoryRegion::new(
regions.push(MemoryRegion::new(
fb.address,
(fb.width * fb.height * fb.bpp + 7) / 8, // round up when divide with 8 (bits/Byte)
MemoryRegionType::Framebuffer,
@ -186,14 +154,13 @@ fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
fn __kernel_start();
fn __kernel_end();
}
regions_unusable.push(MemoryRegion::new(
regions.push(MemoryRegion::new(
__kernel_start as usize,
__kernel_end as usize - __kernel_start as usize,
MemoryRegionType::Kernel,
));
// Add the initramfs area.
// These are physical addresses provided by the linker script.
if info.mods_count != 0 {
let modules_addr = info.mods_addr as usize;
// We only use one module
@ -203,33 +170,15 @@ fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
(*(paddr_to_vaddr(modules_addr + 4) as *const u32)) as usize,
)
};
regions_unusable.push(MemoryRegion::new(
regions.push(MemoryRegion::new(
start,
end - start,
MemoryRegionType::Reserved,
MemoryRegionType::Module,
));
}
// `regions_*` are 2 rolling vectors since we are going to truncate
// the regions in a iterative manner.
let mut regions = Vec::<MemoryRegion>::new();
let regions_src = &mut regions_usable;
let regions_dst = &mut regions;
// Truncate the usable regions.
for &r_unusable in &regions_unusable {
regions_dst.clear();
for r_usable in &*regions_src {
regions_dst.append(&mut r_usable.truncate(&r_unusable));
}
swap(regions_src, regions_dst);
}
// Initialize with regions_unusable + regions_src
memory_regions.call_once(move || {
let mut all_regions = regions_unusable;
all_regions.append(&mut regions_usable);
all_regions
});
// Initialize with non-overlapping regions.
memory_regions.call_once(move || non_overlapping_regions_from(regions.as_ref()));
}
/// Representation of Multiboot Information according to specification.
@ -405,8 +354,18 @@ extern "Rust" {
static MB1_INFO: Once<&'static MultibootLegacyInfo> = Once::new();
pub(super) unsafe fn multiboot_entry(boot_magic: u32, boot_params: u64) -> ! {
/// The entry point of Rust code called by inline asm.
#[no_mangle]
unsafe extern "sysv64" fn __multiboot_entry(boot_magic: u32, boot_params: u64) -> ! {
assert_eq!(boot_magic, MULTIBOOT_ENTRY_MAGIC);
MB1_INFO.call_once(|| &*(paddr_to_vaddr(boot_params as usize) as *const MultibootLegacyInfo));
crate::boot::register_boot_init_callbacks(
init_bootloader_name,
init_kernel_commandline,
init_initramfs,
init_acpi_arg,
init_framebuffer_info,
init_memory_regions,
);
jinux_main();
}

View File

@ -0,0 +1,44 @@
// This is the GNU Multiboot 2 header.
// Reference: https://www.gnu.org/software/grub/manual/multiboot2/html_node/Index.html//Index
.section ".multiboot2_header", "a"
.code32
// Macros for cleaner code in the header fields.
MB2_MAGIC = 0xE85250D6
MB2_ARCHITECTURE = 0 // 32-bit (protected) mode of i386
MB2_HEADERLEN = header_end - header_start
MB2_CHECKSUM = -(MB2_MAGIC + MB2_ARCHITECTURE + MB2_HEADERLEN)
header_start:
.align 8
.long MB2_MAGIC
.long MB2_ARCHITECTURE
.long MB2_HEADERLEN
.long MB2_CHECKSUM
// Tag: entry address
entry_address_tag_start:
.short 3
.short 1 // Optional
.long entry_address_tag_end - entry_address_tag_start
.extern __multiboot2_boot
.long __multiboot2_boot // entry_addr
entry_address_tag_end:
// Tag: information request
.align 8
info_request:
.short 1
.short 0 // Required
.long info_request_end - info_request
.long 6 // Memory map request
.long 15 // ACPI (new) request
info_request_end:
// Tag: header end
.align 8
.short 0 // type: tags end
.short 0 // flags
.long 8 // size
header_end:

View File

@ -6,20 +6,19 @@ use multiboot2::{BootInformation, BootInformationHeader, MemoryAreaType};
use crate::boot::{
kcmdline::KCmdlineArg,
memory_region::{MemoryRegion, MemoryRegionType},
memory_region::{non_overlapping_regions_from, MemoryRegion, MemoryRegionType},
BootloaderAcpiArg, BootloaderFramebufferArg,
};
use core::mem::swap;
use spin::Once;
use core::arch::global_asm;
global_asm!(include_str!("header.S"));
use crate::{config::PHYS_OFFSET, vm::paddr_to_vaddr};
pub(super) const MULTIBOOT2_ENTRY_MAGIC: u32 = 0x36d76289;
pub(super) fn boot_by_multiboot2() -> bool {
MB2_INFO.is_completed()
}
static MB2_INFO: Once<BootInformation> = Once::new();
fn init_bootloader_name(bootloader_name: &'static Once<String>) {
@ -103,12 +102,7 @@ impl From<MemoryAreaType> for MemoryRegionType {
}
fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
// We should later use regions in `regions_unusable` to truncate all
// regions in `regions_usable`.
// The difference is that regions in `regions_usable` could be used by
// the frame allocator.
let mut regions_usable = Vec::<MemoryRegion>::new();
let mut regions_unusable = Vec::<MemoryRegion>::new();
let mut regions = Vec::<MemoryRegion>::new();
// Add the regions returned by Grub.
let memory_regions_tag = MB2_INFO
@ -126,14 +120,7 @@ fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
(end - start).try_into().unwrap(),
area_typ,
);
match area_typ {
MemoryRegionType::Usable | MemoryRegionType::Reclaimable => {
regions_usable.push(region);
}
_ => {
regions_unusable.push(region);
}
}
regions.push(region);
}
// Add the framebuffer region since Grub does not specify it.
let fb_tag = MB2_INFO.get().unwrap().framebuffer_tag().unwrap().unwrap();
@ -143,7 +130,7 @@ fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
height: fb_tag.height() as usize,
bpp: fb_tag.bpp() as usize,
};
regions_unusable.push(MemoryRegion::new(
regions.push(MemoryRegion::new(
fb.address,
(fb.width * fb.height * fb.bpp + 7) / 8, // round up when divide with 8 (bits/Byte)
MemoryRegionType::Framebuffer,
@ -154,7 +141,7 @@ fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
fn __kernel_start();
fn __kernel_end();
}
regions_unusable.push(MemoryRegion::new(
regions.push(MemoryRegion::new(
__kernel_start as usize,
__kernel_end as usize - __kernel_start as usize,
MemoryRegionType::Kernel,
@ -162,51 +149,15 @@ fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
// Add the boot module region since Grub does not specify it.
let mb2_module_tag = MB2_INFO.get().unwrap().module_tags();
for m in mb2_module_tag {
regions_unusable.push(MemoryRegion::new(
regions.push(MemoryRegion::new(
m.start_address() as usize,
m.module_size() as usize,
MemoryRegionType::Module,
));
}
// `regions_*` are 2 rolling vectors since we are going to truncate
// the regions in a iterative manner.
let mut regions = Vec::<MemoryRegion>::new();
let regions_src = &mut regions_usable;
let regions_dst = &mut regions;
// Truncate the usable regions.
for &r_unusable in &regions_unusable {
regions_dst.clear();
for r_usable in &*regions_src {
regions_dst.append(&mut r_usable.truncate(&r_unusable));
}
swap(regions_src, regions_dst);
}
// Initialize with regions_unusable + regions_src
memory_regions.call_once(move || {
let mut all_regions = regions_unusable;
all_regions.append(regions_src);
all_regions
});
}
/// Initialize the global boot static varaiables in the boot module to allow
/// other modules to get the boot information.
pub fn init_boot_args(
bootloader_name: &'static Once<String>,
kernel_cmdline: &'static Once<KCmdlineArg>,
initramfs: &'static Once<&'static [u8]>,
acpi: &'static Once<BootloaderAcpiArg>,
framebuffer_arg: &'static Once<BootloaderFramebufferArg>,
memory_regions: &'static Once<Vec<MemoryRegion>>,
) {
init_bootloader_name(bootloader_name);
init_kernel_commandline(kernel_cmdline);
init_initramfs(initramfs);
init_acpi_arg(acpi);
init_framebuffer_info(framebuffer_arg);
init_memory_regions(memory_regions);
// Initialize with non-overlapping regions.
memory_regions.call_once(move || non_overlapping_regions_from(regions.as_ref()));
}
// The entry point of kernel code, which should be defined by the package that
@ -216,10 +167,19 @@ extern "Rust" {
}
/// The entry point of Rust code called by inline asm.
pub(super) unsafe fn multiboot2_entry(boot_magic: u32, boot_params: u64) -> ! {
#[no_mangle]
unsafe extern "sysv64" fn __multiboot2_entry(boot_magic: u32, boot_params: u64) -> ! {
assert_eq!(boot_magic, MULTIBOOT2_ENTRY_MAGIC);
MB2_INFO.call_once(|| unsafe {
BootInformation::load(boot_params as *const BootInformationHeader).unwrap()
});
crate::boot::register_boot_init_callbacks(
init_bootloader_name,
init_kernel_commandline,
init_initramfs,
init_acpi_arg,
init_framebuffer_info,
init_memory_regions,
);
jinux_main();
}

View File

@ -2,6 +2,7 @@
//!
use alloc::{vec, vec::Vec};
use core::mem::swap;
/// The type of initial memory regions that are needed for the kernel.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
@ -34,7 +35,7 @@ pub struct MemoryRegion {
}
impl MemoryRegion {
/// Construct a page aligned memory region.
/// Construct a valid memory region.
pub fn new(base: usize, len: usize, typ: MemoryRegionType) -> Self {
MemoryRegion { base, len, typ }
}
@ -101,3 +102,46 @@ impl MemoryRegion {
}
}
}
/// Truncate regions, resulting in a set of regions that does not overlap.
///
/// The truncation will be done according to the type of the regions, that
/// usable and reclaimable regions will be truncated by the unusable regions.
pub fn non_overlapping_regions_from(regions: &[MemoryRegion]) -> Vec<MemoryRegion> {
// We should later use regions in `regions_unusable` to truncate all
// regions in `regions_usable`.
// The difference is that regions in `regions_usable` could be used by
// the frame allocator.
let mut regions_usable = Vec::<MemoryRegion>::new();
let mut regions_unusable = Vec::<MemoryRegion>::new();
for r in regions {
match r.typ {
MemoryRegionType::Usable | MemoryRegionType::Reclaimable => {
regions_usable.push(*r);
}
_ => {
regions_unusable.push(*r);
}
}
}
// `regions_*` are 2 rolling vectors since we are going to truncate
// the regions in a iterative manner.
let mut regions = Vec::<MemoryRegion>::new();
let regions_src = &mut regions_usable;
let regions_dst = &mut regions;
// Truncate the usable regions.
for &r_unusable in &regions_unusable {
regions_dst.clear();
for r_usable in &*regions_src {
regions_dst.append(&mut r_usable.truncate(&r_unusable));
}
swap(regions_src, regions_dst);
}
// Combine all the regions processed.
let mut all_regions = regions_unusable;
all_regions.append(&mut regions_usable);
all_regions
}

View File

@ -48,11 +48,33 @@ macro_rules! define_global_static_boot_arguments {
}
)*
// Produce a init function call. The init function must
// be defined in the `arch::boot` module conforming to this
// definition.
fn arch_init_boot_args() {
crate::arch::boot::init_boot_args( $( &$upper, )* );
struct BootInitCallBacks {
$( $lower: fn(&'static Once<$typ>) -> (), )*
}
static BOOT_INIT_CALLBACKS: Once<BootInitCallBacks> = Once::new();
/// The macro generated boot init callbacks registering interface.
///
/// For the introduction of a new boot protocol, the entry point could be a novel
/// one. The entry point function should register all the boot initialization
/// methods before `jinux_main` is called. A boot initialization method takes a
/// reference of the global static boot information variable and initialize it,
/// so that the boot information it represents could be accessed in the kernel
/// anywhere.
///
/// The reason why the entry point function is not designed to directly initialize
/// the boot information variables is simply that the heap is not initialized at
/// that moment.
pub fn register_boot_init_callbacks($( $lower: fn(&'static Once<$typ>) -> (), )* ) {
BOOT_INIT_CALLBACKS.call_once(|| {
BootInitCallBacks { $( $lower, )* }
});
}
fn call_all_boot_init_callbacks() {
let callbacks = &BOOT_INIT_CALLBACKS.get().unwrap();
$( (callbacks.$lower)(&$upper); )*
}
};
}
@ -74,5 +96,5 @@ define_global_static_boot_arguments!(
/// The initialization must be done after the heap is set and before physical
/// mappings are cancelled.
pub fn init() {
arch_init_boot_args();
call_all_boot_init_callbacks();
}

View File

@ -38,6 +38,7 @@ RUN apt update && apt-get install -y --no-install-recommends \
gdb \
grub-common \
grub-pc \
grub-pc-dbg \
libssl-dev \
net-tools \
openssh-server \
@ -68,8 +69,9 @@ RUN curl https://sh.rustup.rs -sSf | \
&& cargo -V \
&& rustup component add rust-src rustc-dev llvm-tools-preview
# Install mdbook
RUN cargo install mdbook
# Install cargo tools
RUN cargo install mdbook \
&& cargo install cargo-binutils
# Add the path of jinux tools
ENV PATH="/root/jinux/target/bin:${PATH}"