Refactor build system, boot and added Linux Boot facilities
This commit is contained in:
parent
6ff4601482
commit
7d5295ab25
|
@ -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.
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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",
|
||||
|
|
41
Makefile
41
Makefile
|
@ -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
|
||||
|
|
27
build.rs
27
build.rs
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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(())
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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 ®ions_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();
|
||||
}
|
|
@ -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:
|
|
@ -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 ®ions_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();
|
||||
}
|
|
@ -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 ®ions_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
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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}"
|
||||
|
|
Loading…
Reference in New Issue