Added bootloader
parent
ff9dae24ff
commit
1fd7b7c55f
|
@ -0,0 +1,264 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aes"
|
||||||
|
version = "0.7.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cipher",
|
||||||
|
"cpufeatures",
|
||||||
|
"opaque-debug",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "argon2"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "db4ce4441f99dbd377ca8a8f57b698c44d0d6e712d8329b5040da5a64aa1ce73"
|
||||||
|
dependencies = [
|
||||||
|
"base64ct",
|
||||||
|
"blake2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64ct"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blake2"
|
||||||
|
version = "0.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.10.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cipher"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpufeatures"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.10.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"crypto-common",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "endian-num"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8f59926911ef34d1efb9ea1ee8ca78385df62ce700ccf2bcb149011bd226888"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.168"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linked_list_allocator"
|
||||||
|
version = "0.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286"
|
||||||
|
dependencies = [
|
||||||
|
"spinning_top",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "opaque-debug"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox-path"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_bootloader"
|
||||||
|
version = "1.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"linked_list_allocator",
|
||||||
|
"log",
|
||||||
|
"redox_syscall",
|
||||||
|
"redoxfs",
|
||||||
|
"spin",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.5.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.6.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redoxfs"
|
||||||
|
version = "0.6.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c8756cd15a32143479f4a723ececcbe2b8e422242924b0628a163826aea8c4d"
|
||||||
|
dependencies = [
|
||||||
|
"aes",
|
||||||
|
"argon2",
|
||||||
|
"base64ct",
|
||||||
|
"endian-num",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"redox-path",
|
||||||
|
"redox_syscall",
|
||||||
|
"seahash",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "seahash"
|
||||||
|
version = "4.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spin"
|
||||||
|
version = "0.9.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spinning_top"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b9eb1a2f4c41445a3a0ff9abc5221c5fcd28e1f13cd7c0397706f9ac938ddb0"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "1.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
|
@ -0,0 +1,97 @@
|
||||||
|
#
|
||||||
|
# MARK: meta
|
||||||
|
#
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "redox_bootloader"
|
||||||
|
version = "1.0.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "bootloader"
|
||||||
|
path = "src/main.rs"
|
||||||
|
crate-type = ["staticlib"]
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# MARK: lints
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
[lints.rust]
|
||||||
|
unused_import_braces = "deny"
|
||||||
|
unit_bindings = "deny"
|
||||||
|
single_use_lifetimes = "deny"
|
||||||
|
non_ascii_idents = "deny"
|
||||||
|
macro_use_extern_crate = "deny"
|
||||||
|
elided_lifetimes_in_paths = "deny"
|
||||||
|
absolute_paths_not_starting_with_crate = "deny"
|
||||||
|
explicit_outlives_requirements = "warn"
|
||||||
|
unused_crate_dependencies = "warn"
|
||||||
|
#variant_size_differences = "warn"
|
||||||
|
redundant_lifetimes = "warn"
|
||||||
|
missing_docs = "allow"
|
||||||
|
|
||||||
|
[lints.clippy]
|
||||||
|
needless_return = "allow"
|
||||||
|
new_without_default = "allow"
|
||||||
|
tabs_in_doc_comments = "allow"
|
||||||
|
|
||||||
|
# Extra
|
||||||
|
expect_used = "deny"
|
||||||
|
#unwrap_used = "deny"
|
||||||
|
#panic = "deny"
|
||||||
|
dbg_macro = "deny"
|
||||||
|
allow_attributes = "deny"
|
||||||
|
create_dir = "deny"
|
||||||
|
filetype_is_file = "deny"
|
||||||
|
integer_division = "deny"
|
||||||
|
lossy_float_literal = "deny"
|
||||||
|
map_err_ignore = "deny"
|
||||||
|
mutex_atomic = "deny"
|
||||||
|
needless_raw_strings = "deny"
|
||||||
|
print_stderr = "deny"
|
||||||
|
print_stdout = "deny"
|
||||||
|
str_to_string = "deny"
|
||||||
|
string_add = "deny"
|
||||||
|
string_to_string = "deny"
|
||||||
|
unimplemented = "deny"
|
||||||
|
use_debug = "deny"
|
||||||
|
verbose_file_reads = "deny"
|
||||||
|
#wildcard_enum_match_arm = "deny"
|
||||||
|
|
||||||
|
# Pedantic
|
||||||
|
large_types_passed_by_value = "deny"
|
||||||
|
match_on_vec_items = "deny"
|
||||||
|
|
||||||
|
# Cargo
|
||||||
|
wildcard_dependencies = "deny"
|
||||||
|
negative_feature_names = "deny"
|
||||||
|
redundant_feature_names = "deny"
|
||||||
|
multiple_crate_versions = "deny"
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# MARK: dependencies
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bitflags = "1.3.2"
|
||||||
|
linked_list_allocator = "0.10.5"
|
||||||
|
log = "0.4.17"
|
||||||
|
redox_syscall = "0.5"
|
||||||
|
spin = "0.9.5"
|
||||||
|
|
||||||
|
[dependencies.redoxfs]
|
||||||
|
version = "0.6.0"
|
||||||
|
default-features = false
|
||||||
|
features = ["force-soft", "log"]
|
||||||
|
|
||||||
|
#
|
||||||
|
# MARK: features
|
||||||
|
#
|
||||||
|
|
||||||
|
[features]
|
||||||
|
serial_debug = []
|
|
@ -0,0 +1,57 @@
|
||||||
|
ENTRY(start)
|
||||||
|
OUTPUT_FORMAT(elf32-i386)
|
||||||
|
|
||||||
|
SECTIONS {
|
||||||
|
/* The start address must match bootloader.asm */
|
||||||
|
. = 0x13000;
|
||||||
|
|
||||||
|
. += SIZEOF_HEADERS;
|
||||||
|
. = ALIGN(4096);
|
||||||
|
|
||||||
|
.text : {
|
||||||
|
__text_start = .;
|
||||||
|
*(.text*)
|
||||||
|
. = ALIGN(4096);
|
||||||
|
__text_end = .;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rodata : {
|
||||||
|
__rodata_start = .;
|
||||||
|
*(.rodata*)
|
||||||
|
. = ALIGN(4096);
|
||||||
|
__rodata_end = .;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data : {
|
||||||
|
__data_start = .;
|
||||||
|
*(.data*)
|
||||||
|
. = ALIGN(4096);
|
||||||
|
__data_end = .;
|
||||||
|
__bss_start = .;
|
||||||
|
*(.bss*)
|
||||||
|
. = ALIGN(4096);
|
||||||
|
__bss_end = .;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tdata : {
|
||||||
|
__tdata_start = .;
|
||||||
|
*(.tdata*)
|
||||||
|
. = ALIGN(4096);
|
||||||
|
__tdata_end = .;
|
||||||
|
__tbss_start = .;
|
||||||
|
*(.tbss*)
|
||||||
|
. += 8;
|
||||||
|
. = ALIGN(4096);
|
||||||
|
__tbss_end = .;
|
||||||
|
}
|
||||||
|
|
||||||
|
__end = .;
|
||||||
|
|
||||||
|
/DISCARD/ : {
|
||||||
|
*(.comment*)
|
||||||
|
*(.eh_frame*)
|
||||||
|
*(.gcc_except_table*)
|
||||||
|
*(.note*)
|
||||||
|
*(.rel.eh_frame*)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
[toolchain]
|
||||||
|
channel = "nightly-2024-05-11"
|
||||||
|
components = ["rust-src"]
|
|
@ -0,0 +1,5 @@
|
||||||
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||||
|
pub use self::x86::*;
|
||||||
|
|
||||||
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||||
|
mod x86;
|
|
@ -0,0 +1,31 @@
|
||||||
|
use redoxfs::Disk;
|
||||||
|
|
||||||
|
use crate::os::{Os, OsVideoMode};
|
||||||
|
|
||||||
|
pub(crate) mod x32;
|
||||||
|
pub(crate) mod x64;
|
||||||
|
|
||||||
|
pub unsafe fn paging_create<D: Disk, V: Iterator<Item = OsVideoMode>>(
|
||||||
|
os: &dyn Os<D, V>,
|
||||||
|
kernel_phys: u64,
|
||||||
|
kernel_size: u64,
|
||||||
|
) -> Option<usize> {
|
||||||
|
if crate::KERNEL_64BIT {
|
||||||
|
x64::paging_create(os, kernel_phys, kernel_size)
|
||||||
|
} else {
|
||||||
|
x32::paging_create(os, kernel_phys, kernel_size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn paging_framebuffer<D: Disk, V: Iterator<Item = OsVideoMode>>(
|
||||||
|
os: &dyn Os<D, V>,
|
||||||
|
page_phys: usize,
|
||||||
|
framebuffer_phys: u64,
|
||||||
|
framebuffer_size: u64,
|
||||||
|
) -> Option<u64> {
|
||||||
|
if crate::KERNEL_64BIT {
|
||||||
|
x64::paging_framebuffer(os, page_phys, framebuffer_phys, framebuffer_size)
|
||||||
|
} else {
|
||||||
|
x32::paging_framebuffer(os, page_phys, framebuffer_phys, framebuffer_size)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
use crate::area_add;
|
||||||
|
use crate::os::{Os, OsMemoryEntry, OsMemoryKind, OsVideoMode};
|
||||||
|
use core::slice;
|
||||||
|
use redoxfs::Disk;
|
||||||
|
|
||||||
|
const PAGE_ENTRIES: usize = 1024;
|
||||||
|
const PAGE_SIZE: usize = 4096;
|
||||||
|
pub(crate) const PHYS_OFFSET: u32 = 0x8000_0000;
|
||||||
|
|
||||||
|
unsafe fn paging_allocate<D: Disk, V: Iterator<Item = OsVideoMode>>(
|
||||||
|
os: &dyn Os<D, V>,
|
||||||
|
) -> Option<&'static mut [u32]> {
|
||||||
|
let ptr = os.alloc_zeroed_page_aligned(PAGE_SIZE);
|
||||||
|
if !ptr.is_null() {
|
||||||
|
area_add(OsMemoryEntry {
|
||||||
|
base: ptr as u64,
|
||||||
|
size: PAGE_SIZE as u64,
|
||||||
|
kind: OsMemoryKind::Reclaim,
|
||||||
|
});
|
||||||
|
Some(slice::from_raw_parts_mut(ptr as *mut u32, PAGE_ENTRIES))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn paging_create<D: Disk, V: Iterator<Item = OsVideoMode>>(
|
||||||
|
os: &dyn Os<D, V>,
|
||||||
|
kernel_phys: u64,
|
||||||
|
kernel_size: u64,
|
||||||
|
) -> Option<usize> {
|
||||||
|
let pd = paging_allocate(os)?;
|
||||||
|
//Identity map 1 GiB using 4 MiB pages, also map at PHYS_OFFSET
|
||||||
|
for pd_i in 0..256 {
|
||||||
|
let addr = pd_i as u32 * 0x40_0000;
|
||||||
|
pd[pd_i] = addr | 1 << 7 | 1 << 1 | 1;
|
||||||
|
pd[pd_i + 512] = addr | 1 << 7 | 1 << 1 | 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map kernel_size at kernel offset
|
||||||
|
let mut kernel_mapped = 0;
|
||||||
|
let mut pd_i = 0xC000_0000 / 0x40_0000;
|
||||||
|
while kernel_mapped < kernel_size && pd_i < pd.len() {
|
||||||
|
let pt = paging_allocate(os)?;
|
||||||
|
pd[pd_i] = pt.as_ptr() as u32 | 1 << 1 | 1;
|
||||||
|
pd_i += 1;
|
||||||
|
|
||||||
|
let mut pt_i = 0;
|
||||||
|
while kernel_mapped < kernel_size && pt_i < pt.len() {
|
||||||
|
let addr = kernel_phys + kernel_mapped;
|
||||||
|
pt[pt_i] = addr as u32 | 1 << 1 | 1;
|
||||||
|
pt_i += 1;
|
||||||
|
kernel_mapped += PAGE_SIZE as u64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(kernel_mapped >= kernel_size);
|
||||||
|
|
||||||
|
Some(pd.as_ptr() as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn paging_framebuffer<D: Disk, V: Iterator<Item = OsVideoMode>>(
|
||||||
|
os: &dyn Os<D, V>,
|
||||||
|
page_phys: usize,
|
||||||
|
framebuffer_phys: u64,
|
||||||
|
framebuffer_size: u64,
|
||||||
|
) -> Option<u64> {
|
||||||
|
let framebuffer_virt = 0xD000_0000; // 256 MiB after kernel mapping, but before heap mapping
|
||||||
|
|
||||||
|
let pd = slice::from_raw_parts_mut(page_phys as *mut u32, PAGE_ENTRIES);
|
||||||
|
|
||||||
|
// Map framebuffer_size at framebuffer offset
|
||||||
|
let mut framebuffer_mapped = 0;
|
||||||
|
let mut pd_i = framebuffer_virt / 0x40_0000;
|
||||||
|
while framebuffer_mapped < framebuffer_size && pd_i < pd.len() {
|
||||||
|
let pt = paging_allocate(os)?;
|
||||||
|
pd[pd_i] = pt.as_ptr() as u32 | 1 << 1 | 1;
|
||||||
|
pd_i += 1;
|
||||||
|
|
||||||
|
let mut pt_i = 0;
|
||||||
|
while framebuffer_mapped < framebuffer_size && pt_i < pt.len() {
|
||||||
|
let addr = framebuffer_phys + framebuffer_mapped;
|
||||||
|
pt[pt_i] = addr as u32 | 1 << 1 | 1;
|
||||||
|
pt_i += 1;
|
||||||
|
framebuffer_mapped += PAGE_SIZE as u64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(framebuffer_mapped >= framebuffer_size);
|
||||||
|
|
||||||
|
Some(framebuffer_virt as u64)
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
use core::slice;
|
||||||
|
use redoxfs::Disk;
|
||||||
|
|
||||||
|
use crate::area_add;
|
||||||
|
use crate::os::{Os, OsMemoryEntry, OsMemoryKind, OsVideoMode};
|
||||||
|
|
||||||
|
const ENTRY_ADDRESS_MASK: u64 = 0x000F_FFFF_FFFF_F000;
|
||||||
|
const PAGE_ENTRIES: usize = 512;
|
||||||
|
const PAGE_SIZE: usize = 4096;
|
||||||
|
pub(crate) const PHYS_OFFSET: u64 = 0xFFFF_8000_0000_0000;
|
||||||
|
|
||||||
|
unsafe fn paging_allocate<D: Disk, V: Iterator<Item = OsVideoMode>>(
|
||||||
|
os: &dyn Os<D, V>,
|
||||||
|
) -> Option<&'static mut [u64]> {
|
||||||
|
let ptr = os.alloc_zeroed_page_aligned(PAGE_SIZE);
|
||||||
|
if !ptr.is_null() {
|
||||||
|
area_add(OsMemoryEntry {
|
||||||
|
base: ptr as u64,
|
||||||
|
size: PAGE_SIZE as u64,
|
||||||
|
kind: OsMemoryKind::Reclaim,
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(slice::from_raw_parts_mut(ptr as *mut u64, PAGE_ENTRIES))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const PRESENT: u64 = 1;
|
||||||
|
const WRITABLE: u64 = 1 << 1;
|
||||||
|
const LARGE: u64 = 1 << 7;
|
||||||
|
|
||||||
|
pub unsafe fn paging_create<D: Disk, V: Iterator<Item = OsVideoMode>>(
|
||||||
|
os: &dyn Os<D, V>,
|
||||||
|
kernel_phys: u64,
|
||||||
|
kernel_size: u64,
|
||||||
|
) -> Option<usize> {
|
||||||
|
// Create PML4
|
||||||
|
let pml4 = paging_allocate(os)?;
|
||||||
|
|
||||||
|
{
|
||||||
|
// Create PDP for identity mapping
|
||||||
|
let pdp = paging_allocate(os)?;
|
||||||
|
|
||||||
|
// Link first user and first kernel PML4 entry to PDP
|
||||||
|
pml4[0] = pdp.as_ptr() as u64 | WRITABLE | PRESENT;
|
||||||
|
pml4[256] = pdp.as_ptr() as u64 | WRITABLE | PRESENT;
|
||||||
|
|
||||||
|
// Identity map 8 GiB using 2 MiB pages
|
||||||
|
for pdp_i in 0..8 {
|
||||||
|
let pd = paging_allocate(os)?;
|
||||||
|
pdp[pdp_i] = pd.as_ptr() as u64 | WRITABLE | PRESENT;
|
||||||
|
for pd_i in 0..pd.len() {
|
||||||
|
let addr = pdp_i as u64 * 0x4000_0000 + pd_i as u64 * 0x20_0000;
|
||||||
|
pd[pd_i] = addr | LARGE | WRITABLE | PRESENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Create PDP (spanning 512 GiB) for kernel mapping
|
||||||
|
let pdp = paging_allocate(os)?;
|
||||||
|
|
||||||
|
// Link last PML4 entry to PDP
|
||||||
|
pml4[511] = pdp.as_ptr() as u64 | WRITABLE | PRESENT;
|
||||||
|
|
||||||
|
// Create PD (spanning 1 GiB) for kernel mapping.
|
||||||
|
let pd = paging_allocate(os)?;
|
||||||
|
|
||||||
|
// The kernel is mapped at -2^31, i.e. 0xFFFF_FFFF_8000_0000. Since a PD is 1 GiB, link
|
||||||
|
// the second last PDP entry to PD.
|
||||||
|
pdp[510] = pd.as_ptr() as u64 | WRITABLE | PRESENT;
|
||||||
|
|
||||||
|
// Map kernel_size bytes to kernel offset, i.e. to the start of the PD.
|
||||||
|
|
||||||
|
let mut kernel_mapped = 0;
|
||||||
|
|
||||||
|
let mut pd_idx = 0;
|
||||||
|
while kernel_mapped < kernel_size && pd_idx < pd.len() {
|
||||||
|
let pt = paging_allocate(os)?;
|
||||||
|
pd[pd_idx] = pt.as_ptr() as u64 | WRITABLE | PRESENT;
|
||||||
|
pd_idx += 1;
|
||||||
|
|
||||||
|
let mut pt_idx = 0;
|
||||||
|
while kernel_mapped < kernel_size && pt_idx < pt.len() {
|
||||||
|
let addr = kernel_phys + kernel_mapped;
|
||||||
|
pt[pt_idx] = addr | WRITABLE | PRESENT;
|
||||||
|
pt_idx += 1;
|
||||||
|
kernel_mapped += PAGE_SIZE as u64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(kernel_mapped >= kernel_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(pml4.as_ptr() as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn paging_framebuffer<D: Disk, V: Iterator<Item = OsVideoMode>>(
|
||||||
|
os: &dyn Os<D, V>,
|
||||||
|
page_phys: usize,
|
||||||
|
framebuffer_phys: u64,
|
||||||
|
framebuffer_size: u64,
|
||||||
|
) -> Option<u64> {
|
||||||
|
//TODO: smarter test for framebuffer already mapped
|
||||||
|
if framebuffer_phys + framebuffer_size <= 0x2_0000_0000 {
|
||||||
|
return Some(framebuffer_phys + PHYS_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
let pml4_i = ((framebuffer_phys / 0x80_0000_0000) + 256) as usize;
|
||||||
|
let mut pdp_i = ((framebuffer_phys % 0x80_0000_0000) / 0x4000_0000) as usize;
|
||||||
|
let mut pd_i = ((framebuffer_phys % 0x4000_0000) / 0x20_0000) as usize;
|
||||||
|
assert_eq!(framebuffer_phys % 0x20_0000, 0);
|
||||||
|
|
||||||
|
let pml4 = slice::from_raw_parts_mut(page_phys as *mut u64, PAGE_ENTRIES);
|
||||||
|
|
||||||
|
// Create PDP for framebuffer mapping
|
||||||
|
let pdp = if pml4[pml4_i] == 0 {
|
||||||
|
let pdp = paging_allocate(os)?;
|
||||||
|
pml4[pml4_i] = pdp.as_ptr() as u64 | 1 << 1 | 1;
|
||||||
|
pdp
|
||||||
|
} else {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
(pml4[pml4_i] & ENTRY_ADDRESS_MASK) as *mut u64,
|
||||||
|
PAGE_ENTRIES,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Map framebuffer_size at framebuffer offset
|
||||||
|
let mut framebuffer_mapped = 0;
|
||||||
|
while framebuffer_mapped < framebuffer_size && pdp_i < pdp.len() {
|
||||||
|
let pd = paging_allocate(os)?;
|
||||||
|
assert_eq!(pdp[pdp_i], 0);
|
||||||
|
pdp[pdp_i] = pd.as_ptr() as u64 | 1 << 1 | 1;
|
||||||
|
|
||||||
|
while framebuffer_mapped < framebuffer_size && pd_i < pd.len() {
|
||||||
|
let addr = framebuffer_phys + framebuffer_mapped;
|
||||||
|
assert_eq!(pd[pd_i], 0);
|
||||||
|
pd[pd_i] = addr | 1 << 7 | 1 << 1 | 1;
|
||||||
|
framebuffer_mapped += 0x20_0000;
|
||||||
|
pd_i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdp_i += 1;
|
||||||
|
pd_i = 0;
|
||||||
|
}
|
||||||
|
assert!(framebuffer_mapped >= framebuffer_size);
|
||||||
|
|
||||||
|
Some(framebuffer_phys + PHYS_OFFSET)
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
use log::{LevelFilter, Log, Metadata, Record};
|
||||||
|
|
||||||
|
pub static LOGGER: Logger = Logger;
|
||||||
|
|
||||||
|
pub struct Logger;
|
||||||
|
|
||||||
|
impl Logger {
|
||||||
|
pub fn init(&'static self) {
|
||||||
|
log::set_logger(self).unwrap();
|
||||||
|
log::set_max_level(LevelFilter::Info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Log for Logger {
|
||||||
|
fn enabled(&self, _metadata: &Metadata<'_>) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(&self, record: &Record<'_>) {
|
||||||
|
if self.enabled(record.metadata()) {
|
||||||
|
println!("{} - {}", record.level(), record.args());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&self) {}
|
||||||
|
}
|
|
@ -0,0 +1,642 @@
|
||||||
|
#![no_std]
|
||||||
|
#![feature(alloc_error_handler)]
|
||||||
|
#![feature(int_roundings)]
|
||||||
|
#![feature(lang_items)]
|
||||||
|
#![allow(internal_features)]
|
||||||
|
#![feature(let_chains)]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
use alloc::{format, string::String, vec::Vec};
|
||||||
|
use core::{
|
||||||
|
cmp,
|
||||||
|
fmt::{self, Write},
|
||||||
|
mem, ptr, slice, str,
|
||||||
|
};
|
||||||
|
use redoxfs::Disk;
|
||||||
|
|
||||||
|
use self::arch::{paging_create, paging_framebuffer};
|
||||||
|
use self::os::{Os, OsHwDesc, OsKey, OsMemoryEntry, OsMemoryKind, OsVideoMode};
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod os;
|
||||||
|
|
||||||
|
mod arch;
|
||||||
|
mod logger;
|
||||||
|
mod serial_16550;
|
||||||
|
|
||||||
|
const KIBI: usize = 1024;
|
||||||
|
const MIBI: usize = KIBI * KIBI;
|
||||||
|
|
||||||
|
//TODO: allocate this in a more reasonable manner
|
||||||
|
static mut AREAS: [OsMemoryEntry; 1024] = [OsMemoryEntry {
|
||||||
|
base: 0,
|
||||||
|
size: 0,
|
||||||
|
kind: OsMemoryKind::Null,
|
||||||
|
}; 1024];
|
||||||
|
static mut AREAS_LEN: usize = 0;
|
||||||
|
|
||||||
|
pub fn area_add(area: OsMemoryEntry) {
|
||||||
|
unsafe {
|
||||||
|
for existing_area in &mut AREAS[0..AREAS_LEN] {
|
||||||
|
if existing_area.kind == area.kind {
|
||||||
|
if existing_area.base.unchecked_add(existing_area.size) == area.base {
|
||||||
|
existing_area.size += area.size;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if area.base.unchecked_add(area.size) == existing_area.base {
|
||||||
|
existing_area.base = area.base;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*AREAS.get_mut(AREAS_LEN).expect("AREAS overflowed!") = area;
|
||||||
|
AREAS_LEN += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static mut KERNEL_64BIT: bool = false;
|
||||||
|
|
||||||
|
pub static mut LIVE_OPT: Option<(u64, &'static [u8])> = None;
|
||||||
|
|
||||||
|
struct SliceWriter<'a> {
|
||||||
|
slice: &'a mut [u8],
|
||||||
|
i: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for SliceWriter<'_> {
|
||||||
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
|
for b in s.bytes() {
|
||||||
|
if let Some(slice_b) = self.slice.get_mut(self.i) {
|
||||||
|
*slice_b = b;
|
||||||
|
self.i += 1;
|
||||||
|
} else {
|
||||||
|
return Err(fmt::Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[repr(C, packed(8))]
|
||||||
|
pub struct KernelArgs {
|
||||||
|
kernel_base: u64,
|
||||||
|
kernel_size: u64,
|
||||||
|
stack_base: u64,
|
||||||
|
stack_size: u64,
|
||||||
|
env_base: u64,
|
||||||
|
env_size: u64,
|
||||||
|
|
||||||
|
/// The base pointer to the saved RSDP.
|
||||||
|
///
|
||||||
|
/// This field can be NULL, and if so, the system has not booted with UEFI or in some other way
|
||||||
|
/// retrieved the RSDPs. The kernel or a userspace driver will thus try searching the BIOS
|
||||||
|
/// memory instead. On UEFI systems, searching is not guaranteed to actually work though.
|
||||||
|
acpi_rsdp_base: u64,
|
||||||
|
/// The size of the RSDP region.
|
||||||
|
acpi_rsdp_size: u64,
|
||||||
|
|
||||||
|
areas_base: u64,
|
||||||
|
areas_size: u64,
|
||||||
|
|
||||||
|
bootstrap_base: u64,
|
||||||
|
bootstrap_size: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn select_mode<D: Disk, V: Iterator<Item = OsVideoMode>>(
|
||||||
|
os: &dyn Os<D, V>,
|
||||||
|
output_i: usize,
|
||||||
|
) -> Option<OsVideoMode> {
|
||||||
|
let mut modes = Vec::new();
|
||||||
|
for mode in os.video_modes(output_i) {
|
||||||
|
let mut aspect_w = mode.width;
|
||||||
|
let mut aspect_h = mode.height;
|
||||||
|
for i in 2..cmp::min(aspect_w / 2, aspect_h / 2) {
|
||||||
|
while aspect_w % i == 0 && aspect_h % i == 0 {
|
||||||
|
aspect_w /= i;
|
||||||
|
aspect_h /= i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modes.push((
|
||||||
|
mode,
|
||||||
|
format!(
|
||||||
|
"{:>4}x{:<4} {:>3}:{:<3}",
|
||||||
|
mode.width, mode.height, aspect_w, aspect_h
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if modes.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort modes by pixel area, reversed
|
||||||
|
modes.sort_by(|a, b| (b.0.width * b.0.height).cmp(&(a.0.width * a.0.height)));
|
||||||
|
|
||||||
|
// Set selected based on best resolution
|
||||||
|
print!("Output {}", output_i);
|
||||||
|
let mut selected = modes.get(0).map_or(0, |x| x.0.id);
|
||||||
|
if let Some((best_width, best_height)) = os.best_resolution(output_i) {
|
||||||
|
print!(", best resolution: {}x{}", best_width, best_height);
|
||||||
|
for (mode, _text) in modes.iter() {
|
||||||
|
if mode.width == best_width && mode.height == best_height {
|
||||||
|
selected = mode.id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
|
||||||
|
println!("Arrow keys and enter select mode");
|
||||||
|
println!();
|
||||||
|
print!(" ");
|
||||||
|
|
||||||
|
let (off_x, off_y) = os.get_text_position();
|
||||||
|
let rows = 12;
|
||||||
|
let mut mode_opt = None;
|
||||||
|
while !modes.is_empty() {
|
||||||
|
let mut row = 0;
|
||||||
|
let mut col = 0;
|
||||||
|
for (mode, text) in modes.iter() {
|
||||||
|
if row >= rows {
|
||||||
|
col += 1;
|
||||||
|
row = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
os.set_text_position(off_x + col * 20, off_y + row);
|
||||||
|
os.set_text_highlight(mode.id == selected);
|
||||||
|
|
||||||
|
print!("{}", text);
|
||||||
|
|
||||||
|
row += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read keypress
|
||||||
|
match os.get_key() {
|
||||||
|
OsKey::Left => {
|
||||||
|
if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == selected) {
|
||||||
|
if mode_i < rows {
|
||||||
|
while mode_i < modes.len() {
|
||||||
|
mode_i += rows;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mode_i -= rows;
|
||||||
|
if let Some(new) = modes.get(mode_i) {
|
||||||
|
selected = new.0.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OsKey::Right => {
|
||||||
|
if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == selected) {
|
||||||
|
mode_i += rows;
|
||||||
|
if mode_i >= modes.len() {
|
||||||
|
mode_i = mode_i % rows;
|
||||||
|
}
|
||||||
|
if let Some(new) = modes.get(mode_i) {
|
||||||
|
selected = new.0.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OsKey::Up => {
|
||||||
|
if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == selected) {
|
||||||
|
if mode_i % rows == 0 {
|
||||||
|
mode_i += rows;
|
||||||
|
if mode_i > modes.len() {
|
||||||
|
mode_i = modes.len();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mode_i -= 1;
|
||||||
|
if let Some(new) = modes.get(mode_i) {
|
||||||
|
selected = new.0.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OsKey::Down => {
|
||||||
|
if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == selected) {
|
||||||
|
mode_i += 1;
|
||||||
|
if mode_i % rows == 0 {
|
||||||
|
mode_i -= rows;
|
||||||
|
}
|
||||||
|
if mode_i >= modes.len() {
|
||||||
|
mode_i = mode_i - mode_i % rows;
|
||||||
|
}
|
||||||
|
if let Some(new) = modes.get(mode_i) {
|
||||||
|
selected = new.0.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OsKey::Enter => {
|
||||||
|
if let Some(mode_i) = modes.iter().position(|x| x.0.id == selected) {
|
||||||
|
if let Some((mode, _text)) = modes.get(mode_i) {
|
||||||
|
mode_opt = Some(*mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
os.set_text_position(0, off_y + rows);
|
||||||
|
os.set_text_highlight(false);
|
||||||
|
println!();
|
||||||
|
|
||||||
|
mode_opt
|
||||||
|
}
|
||||||
|
|
||||||
|
fn redoxfs<D: Disk, V: Iterator<Item = OsVideoMode>>(
|
||||||
|
os: &dyn Os<D, V>,
|
||||||
|
) -> (redoxfs::FileSystem<D>, Option<&'static [u8]>) {
|
||||||
|
let attempts = 10;
|
||||||
|
for attempt in 0..=attempts {
|
||||||
|
let mut password_opt = None;
|
||||||
|
if attempt > 0 {
|
||||||
|
print!("\rRedoxFS password ({}/{}): ", attempt, attempts);
|
||||||
|
|
||||||
|
let mut password = String::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match os.get_key() {
|
||||||
|
OsKey::Backspace | OsKey::Delete => {
|
||||||
|
if !password.is_empty() {
|
||||||
|
print!("\x08 \x08");
|
||||||
|
password.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OsKey::Char(c) => {
|
||||||
|
print!("*");
|
||||||
|
password.push(c)
|
||||||
|
}
|
||||||
|
OsKey::Enter => break,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erase password information
|
||||||
|
while os.get_text_position().0 > 0 {
|
||||||
|
print!("\x08 \x08");
|
||||||
|
}
|
||||||
|
|
||||||
|
if !password.is_empty() {
|
||||||
|
password_opt = Some(password);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match os.filesystem(password_opt.as_ref().map(|x| x.as_bytes())) {
|
||||||
|
Ok(fs) => {
|
||||||
|
return (
|
||||||
|
fs,
|
||||||
|
password_opt.map(|password| {
|
||||||
|
// Copy password to page aligned memory
|
||||||
|
let password_size = password.len();
|
||||||
|
let password_base = os.alloc_zeroed_page_aligned(password_size);
|
||||||
|
unsafe {
|
||||||
|
ptr::copy(password.as_ptr(), password_base, password_size);
|
||||||
|
slice::from_raw_parts(password_base, password_size)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(err) => match err.errno {
|
||||||
|
// Incorrect password, try again
|
||||||
|
syscall::ENOKEY => (),
|
||||||
|
_ => {
|
||||||
|
panic!("Failed to open RedoxFS: {}", err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic!("RedoxFS out of unlock attempts");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
enum Filetype {
|
||||||
|
Elf,
|
||||||
|
Initfs,
|
||||||
|
}
|
||||||
|
fn load_to_memory<D: Disk>(
|
||||||
|
os: &dyn Os<D, impl Iterator<Item = OsVideoMode>>,
|
||||||
|
fs: &mut redoxfs::FileSystem<D>,
|
||||||
|
dirname: &str,
|
||||||
|
filename: &str,
|
||||||
|
filetype: Filetype,
|
||||||
|
) -> &'static mut [u8] {
|
||||||
|
fs.tx(|tx| {
|
||||||
|
let dir_node = tx
|
||||||
|
.find_node(redoxfs::TreePtr::root(), dirname)
|
||||||
|
.unwrap_or_else(|err| panic!("Failed to find {} directory: {}", dirname, err));
|
||||||
|
|
||||||
|
let node = tx
|
||||||
|
.find_node(dir_node.ptr(), filename)
|
||||||
|
.unwrap_or_else(|err| panic!("Failed to find {} file: {}", filename, err));
|
||||||
|
|
||||||
|
let size = node.data().size();
|
||||||
|
|
||||||
|
print!("{}: 0/{} MiB", filename, size / MIBI as u64);
|
||||||
|
|
||||||
|
let ptr = os.alloc_zeroed_page_aligned(size as usize);
|
||||||
|
if ptr.is_null() {
|
||||||
|
panic!("Failed to allocate memory for {}", filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
let slice = unsafe { slice::from_raw_parts_mut(ptr, size as usize) };
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
for chunk in slice.chunks_mut(MIBI) {
|
||||||
|
print!(
|
||||||
|
"\r{}: {}/{} MiB",
|
||||||
|
filename,
|
||||||
|
i / MIBI as u64,
|
||||||
|
size / MIBI as u64
|
||||||
|
);
|
||||||
|
i +=
|
||||||
|
tx.read_node_inner(&node, i, chunk)
|
||||||
|
.unwrap_or_else(|err| panic!("Failed to read `{}` file: {}", filename, err)) as u64;
|
||||||
|
}
|
||||||
|
println!(
|
||||||
|
"\r{}: {}/{} MiB",
|
||||||
|
filename,
|
||||||
|
i / MIBI as u64,
|
||||||
|
size / MIBI as u64
|
||||||
|
);
|
||||||
|
|
||||||
|
if filetype == Filetype::Elf {
|
||||||
|
let magic = &slice[..4];
|
||||||
|
if magic != b"\x7FELF" {
|
||||||
|
panic!("{} has invalid magic number {:#X?}", filename, magic);
|
||||||
|
}
|
||||||
|
} else if filetype == Filetype::Initfs {
|
||||||
|
let magic = &slice[..8];
|
||||||
|
if magic != b"RedoxFtw" {
|
||||||
|
panic!("{} has invalid magic number {:#X?}", filename, magic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(slice)
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|err| {
|
||||||
|
panic!(
|
||||||
|
"RedoxFS transaction failed while loading `{}`: {}",
|
||||||
|
filename, err
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn elf_entry(data: &[u8]) -> (u64, bool) {
|
||||||
|
match (data[4], data[5]) {
|
||||||
|
// 32-bit, little endian
|
||||||
|
(1, 1) => (
|
||||||
|
u32::from_le_bytes(
|
||||||
|
<[u8; 4]>::try_from(&data[0x18..0x18 + 4]).expect("conversion cannot fail"),
|
||||||
|
) as u64,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
// 32-bit, big endian
|
||||||
|
(1, 2) => (
|
||||||
|
u32::from_be_bytes(
|
||||||
|
<[u8; 4]>::try_from(&data[0x18..0x18 + 4]).expect("conversion cannot fail"),
|
||||||
|
) as u64,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
// 64-bit, little endian
|
||||||
|
(2, 1) => (
|
||||||
|
u64::from_le_bytes(
|
||||||
|
<[u8; 8]>::try_from(&data[0x18..0x18 + 8]).expect("conversion cannot fail"),
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
// 64-bit, big endian
|
||||||
|
(2, 2) => (
|
||||||
|
u64::from_be_bytes(
|
||||||
|
<[u8; 8]>::try_from(&data[0x18..0x18 + 8]).expect("conversion cannot fail"),
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
(ei_class, ei_data) => {
|
||||||
|
panic!("Unsupported ELF EI_CLASS {} EI_DATA {}", ei_class, ei_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main<D: Disk, V: Iterator<Item = OsVideoMode>>(os: &dyn Os<D, V>) -> (usize, u64, KernelArgs) {
|
||||||
|
println!(
|
||||||
|
"Redox OS Bootloader {} on {}",
|
||||||
|
env!("CARGO_PKG_VERSION"),
|
||||||
|
os.name()
|
||||||
|
);
|
||||||
|
|
||||||
|
let hwdesc = os.hwdesc();
|
||||||
|
println!("Hardware descriptor: {:x?}", hwdesc);
|
||||||
|
let (acpi_rsdp_base, acpi_rsdp_size) = match hwdesc {
|
||||||
|
OsHwDesc::Acpi(base, size) => (base, size),
|
||||||
|
OsHwDesc::DeviceTree(base, size) => (base, size),
|
||||||
|
OsHwDesc::NotFound => (0, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (mut fs, password_opt) = redoxfs(os);
|
||||||
|
|
||||||
|
print!("RedoxFS ");
|
||||||
|
for i in 0..fs.header.uuid().len() {
|
||||||
|
if i == 4 || i == 6 || i == 8 || i == 10 {
|
||||||
|
print!("-");
|
||||||
|
}
|
||||||
|
|
||||||
|
print!("{:>02x}", fs.header.uuid()[i]);
|
||||||
|
}
|
||||||
|
println!(": {} MiB", fs.header.size() / MIBI as u64);
|
||||||
|
println!();
|
||||||
|
|
||||||
|
let mut mode_opts = Vec::new();
|
||||||
|
for output_i in 0..os.video_outputs() {
|
||||||
|
if output_i > 0 {
|
||||||
|
os.clear_text();
|
||||||
|
}
|
||||||
|
mode_opts.push(select_mode(os, output_i));
|
||||||
|
}
|
||||||
|
|
||||||
|
let stack_size = 128 * KIBI;
|
||||||
|
let stack_base = os.alloc_zeroed_page_aligned(stack_size);
|
||||||
|
if stack_base.is_null() {
|
||||||
|
panic!("Failed to allocate memory for stack");
|
||||||
|
}
|
||||||
|
|
||||||
|
let live_opt = if cfg!(feature = "live") {
|
||||||
|
let size = fs.header.size();
|
||||||
|
|
||||||
|
print!("live: 0/{} MiB", size / MIBI as u64);
|
||||||
|
|
||||||
|
let ptr = os.alloc_zeroed_page_aligned(size as usize);
|
||||||
|
if ptr.is_null() {
|
||||||
|
panic!("Failed to allocate memory for live");
|
||||||
|
}
|
||||||
|
|
||||||
|
let live = unsafe { slice::from_raw_parts_mut(ptr, size as usize) };
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
for chunk in live.chunks_mut(MIBI) {
|
||||||
|
print!("\rlive: {}/{} MiB", i / MIBI as u64, size / MIBI as u64);
|
||||||
|
i += unsafe {
|
||||||
|
fs.disk
|
||||||
|
.read_at(fs.block + i / redoxfs::BLOCK_SIZE, chunk)
|
||||||
|
.expect("Failed to read live disk") as u64
|
||||||
|
};
|
||||||
|
}
|
||||||
|
println!("\rlive: {}/{} MiB", i / MIBI as u64, size / MIBI as u64);
|
||||||
|
|
||||||
|
println!("Switching to live disk");
|
||||||
|
unsafe {
|
||||||
|
LIVE_OPT = Some((fs.block, slice::from_raw_parts_mut(ptr, size as usize)));
|
||||||
|
}
|
||||||
|
|
||||||
|
area_add(OsMemoryEntry {
|
||||||
|
base: live.as_ptr() as u64,
|
||||||
|
size: live.len() as u64,
|
||||||
|
kind: OsMemoryKind::Reserved,
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(live)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let (kernel, kernel_entry) = {
|
||||||
|
let kernel = load_to_memory(os, &mut fs, "boot", "kernel", Filetype::Elf);
|
||||||
|
let (kernel_entry, kernel_64bit) = elf_entry(kernel);
|
||||||
|
unsafe {
|
||||||
|
KERNEL_64BIT = kernel_64bit;
|
||||||
|
}
|
||||||
|
(kernel, kernel_entry)
|
||||||
|
};
|
||||||
|
|
||||||
|
let (bootstrap_size, bootstrap_base) = {
|
||||||
|
let initfs_slice = load_to_memory(os, &mut fs, "boot", "initfs", Filetype::Initfs);
|
||||||
|
|
||||||
|
let memory = unsafe {
|
||||||
|
let total_size = initfs_slice.len().next_multiple_of(4096);
|
||||||
|
let ptr = os.alloc_zeroed_page_aligned(total_size);
|
||||||
|
assert!(!ptr.is_null(), "failed to allocate bootstrap+initfs memory");
|
||||||
|
core::slice::from_raw_parts_mut(ptr, total_size)
|
||||||
|
};
|
||||||
|
memory[..initfs_slice.len()].copy_from_slice(initfs_slice);
|
||||||
|
|
||||||
|
(memory.len() as u64, memory.as_mut_ptr() as u64)
|
||||||
|
};
|
||||||
|
|
||||||
|
let page_phys = unsafe { paging_create(os, kernel.as_ptr() as u64, kernel.len() as u64) }
|
||||||
|
.expect("Failed to set up paging");
|
||||||
|
|
||||||
|
let mut env_size = 64 * KIBI;
|
||||||
|
let env_base = os.alloc_zeroed_page_aligned(env_size);
|
||||||
|
if env_base.is_null() {
|
||||||
|
panic!("Failed to allocate memory for stack");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut w = SliceWriter {
|
||||||
|
slice: unsafe { slice::from_raw_parts_mut(env_base, env_size) },
|
||||||
|
i: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
writeln!(w, "BOOT_MODE={}", os.name()).unwrap();
|
||||||
|
|
||||||
|
match hwdesc {
|
||||||
|
OsHwDesc::Acpi(addr, size) => {
|
||||||
|
writeln!(w, "RSDP_ADDR={:016x}", addr).unwrap();
|
||||||
|
writeln!(w, "RSDP_SIZE={:016x}", size).unwrap();
|
||||||
|
}
|
||||||
|
OsHwDesc::DeviceTree(addr, size) => {
|
||||||
|
writeln!(w, "DTB_ADDR={:016x}", addr).unwrap();
|
||||||
|
writeln!(w, "DTB_SIZE={:016x}", size).unwrap();
|
||||||
|
}
|
||||||
|
OsHwDesc::NotFound => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(live) = live_opt {
|
||||||
|
writeln!(w, "DISK_LIVE_ADDR={:016x}", live.as_ptr() as usize).unwrap();
|
||||||
|
writeln!(w, "DISK_LIVE_SIZE={:016x}", live.len()).unwrap();
|
||||||
|
writeln!(w, "REDOXFS_BLOCK={:016x}", 0).unwrap();
|
||||||
|
} else {
|
||||||
|
writeln!(w, "REDOXFS_BLOCK={:016x}", fs.block).unwrap();
|
||||||
|
}
|
||||||
|
write!(w, "REDOXFS_UUID=").unwrap();
|
||||||
|
for i in 0..fs.header.uuid().len() {
|
||||||
|
if i == 4 || i == 6 || i == 8 || i == 10 {
|
||||||
|
write!(w, "-").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(w, "{:>02x}", fs.header.uuid()[i]).unwrap();
|
||||||
|
}
|
||||||
|
writeln!(w).unwrap();
|
||||||
|
if let Some(password) = password_opt {
|
||||||
|
writeln!(
|
||||||
|
w,
|
||||||
|
"REDOXFS_PASSWORD_ADDR={:016x}",
|
||||||
|
password.as_ptr() as usize
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
writeln!(w, "REDOXFS_PASSWORD_SIZE={:016x}", password.len()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "riscv64")]
|
||||||
|
{
|
||||||
|
let boot_hartid = os::efi_get_boot_hartid()
|
||||||
|
.expect("Could not retrieve boot hart id from EFI implementation!");
|
||||||
|
writeln!(w, "BOOT_HART_ID={:016x}", boot_hartid).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
for output_i in 0..os.video_outputs() {
|
||||||
|
if let Some(mut mode) = mode_opts[output_i] {
|
||||||
|
// Set mode to get updated values
|
||||||
|
os.set_video_mode(output_i, &mut mode);
|
||||||
|
|
||||||
|
if output_i == 0 {
|
||||||
|
let virt = unsafe {
|
||||||
|
paging_framebuffer(
|
||||||
|
os,
|
||||||
|
page_phys,
|
||||||
|
mode.base,
|
||||||
|
(mode.stride * mode.height * 4) as u64,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.expect("Failed to map framebuffer");
|
||||||
|
|
||||||
|
writeln!(w, "FRAMEBUFFER_ADDR={:016x}", mode.base).unwrap();
|
||||||
|
writeln!(w, "FRAMEBUFFER_VIRT={:016x}", virt).unwrap();
|
||||||
|
writeln!(w, "FRAMEBUFFER_WIDTH={:016x}", mode.width).unwrap();
|
||||||
|
writeln!(w, "FRAMEBUFFER_HEIGHT={:016x}", mode.height).unwrap();
|
||||||
|
writeln!(w, "FRAMEBUFFER_STRIDE={:016x}", mode.stride).unwrap();
|
||||||
|
} else {
|
||||||
|
writeln!(
|
||||||
|
w,
|
||||||
|
"FRAMEBUFFER{}={:#x},{},{},{}",
|
||||||
|
output_i, mode.base, mode.width, mode.height, mode.stride,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
env_size = w.i;
|
||||||
|
}
|
||||||
|
|
||||||
|
(
|
||||||
|
page_phys,
|
||||||
|
kernel_entry,
|
||||||
|
KernelArgs {
|
||||||
|
kernel_base: kernel.as_ptr() as u64,
|
||||||
|
kernel_size: kernel.len() as u64,
|
||||||
|
stack_base: stack_base as u64,
|
||||||
|
stack_size: stack_size as u64,
|
||||||
|
env_base: env_base as u64,
|
||||||
|
env_size: env_size as u64,
|
||||||
|
acpi_rsdp_base,
|
||||||
|
acpi_rsdp_size,
|
||||||
|
areas_base: unsafe { AREAS.as_ptr() as u64 },
|
||||||
|
areas_size: unsafe { (AREAS.len() * mem::size_of::<OsMemoryEntry>()) as u64 },
|
||||||
|
bootstrap_base,
|
||||||
|
bootstrap_size,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,175 @@
|
||||||
|
use core::{mem, ptr};
|
||||||
|
use redoxfs::{Disk, BLOCK_SIZE};
|
||||||
|
use syscall::error::{Error, Result, EIO};
|
||||||
|
|
||||||
|
use super::{ThunkData, DISK_ADDRESS_PACKET_ADDR, DISK_BIOS_ADDR};
|
||||||
|
|
||||||
|
const SECTOR_SIZE: u64 = 512;
|
||||||
|
const BLOCKS_PER_SECTOR: u64 = BLOCK_SIZE / SECTOR_SIZE;
|
||||||
|
// 128 sectors is the amount allocated for DISK_BIOS_ADDR
|
||||||
|
// 127 sectors is the maximum for many BIOSes
|
||||||
|
const MAX_SECTORS: u64 = 127;
|
||||||
|
const MAX_BLOCKS: u64 = MAX_SECTORS * SECTOR_SIZE / BLOCK_SIZE;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct DiskAddressPacket {
|
||||||
|
size: u8,
|
||||||
|
reserved: u8,
|
||||||
|
sectors: u16,
|
||||||
|
buffer: u16,
|
||||||
|
segment: u16,
|
||||||
|
address: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiskAddressPacket {
|
||||||
|
pub fn from_block(block: u64, count: u64) -> DiskAddressPacket {
|
||||||
|
let address = block * BLOCKS_PER_SECTOR;
|
||||||
|
let sectors = count * BLOCKS_PER_SECTOR;
|
||||||
|
assert!(sectors <= MAX_SECTORS);
|
||||||
|
DiskAddressPacket {
|
||||||
|
size: mem::size_of::<DiskAddressPacket>() as u8,
|
||||||
|
reserved: 0,
|
||||||
|
sectors: sectors as u16,
|
||||||
|
buffer: (DISK_BIOS_ADDR & 0xF) as u16,
|
||||||
|
segment: (DISK_BIOS_ADDR >> 4) as u16,
|
||||||
|
address,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DiskBios {
|
||||||
|
boot_disk: u8,
|
||||||
|
thunk13: extern "C" fn(),
|
||||||
|
chs_opt: Option<(u32, u32, u32)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiskBios {
|
||||||
|
pub fn new(boot_disk: u8, thunk13: extern "C" fn()) -> Self {
|
||||||
|
let chs_opt = unsafe {
|
||||||
|
let mut data = ThunkData::new();
|
||||||
|
data.eax = 0x4100;
|
||||||
|
data.ebx = 0x55AA;
|
||||||
|
data.edx = boot_disk as u32;
|
||||||
|
|
||||||
|
data.with(thunk13);
|
||||||
|
|
||||||
|
if (data.ebx & 0xFFFF) == 0xAA55 {
|
||||||
|
// Extensions are installed, do not use CHS
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
// Extensions are not installed, get CHS geometry
|
||||||
|
data = ThunkData::new();
|
||||||
|
data.eax = 0x0800;
|
||||||
|
data.edx = boot_disk as u32;
|
||||||
|
data.edi = 0;
|
||||||
|
|
||||||
|
data.with(thunk13);
|
||||||
|
|
||||||
|
//TODO: return result on error
|
||||||
|
let ah = ({ data.eax } >> 8) & 0xFF;
|
||||||
|
assert_eq!(ah, 0);
|
||||||
|
|
||||||
|
let c = (data.ecx >> 8) & 0xFF | ((data.ecx >> 6) & 0x3) << 8;
|
||||||
|
let h = ((data.edx >> 8) & 0xFF) + 1;
|
||||||
|
let s = data.ecx & 0x3F;
|
||||||
|
|
||||||
|
Some((c, h, s))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
boot_disk,
|
||||||
|
thunk13,
|
||||||
|
chs_opt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Disk for DiskBios {
|
||||||
|
unsafe fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
|
||||||
|
// Optimization for live disks
|
||||||
|
if let Some(live) = crate::LIVE_OPT {
|
||||||
|
if block >= live.0 {
|
||||||
|
let start = ((block - live.0) * BLOCK_SIZE) as usize;
|
||||||
|
let end = start + buffer.len();
|
||||||
|
if end <= live.1.len() {
|
||||||
|
buffer.copy_from_slice(&live.1[start..end]);
|
||||||
|
return Ok(buffer.len());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, chunk) in buffer
|
||||||
|
.chunks_mut((MAX_BLOCKS * BLOCK_SIZE) as usize)
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
let dap = DiskAddressPacket::from_block(
|
||||||
|
block + i as u64 * MAX_BLOCKS,
|
||||||
|
chunk.len() as u64 / BLOCK_SIZE,
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some((_, h_max, s_max)) = self.chs_opt {
|
||||||
|
let s = (dap.address % s_max as u64) + 1;
|
||||||
|
assert!(s <= 63, "invalid sector {}", s);
|
||||||
|
|
||||||
|
let tmp = dap.address / s_max as u64;
|
||||||
|
let h = tmp % h_max as u64;
|
||||||
|
assert!(h <= 255, "invalid head {}", h);
|
||||||
|
|
||||||
|
let c = tmp / h_max as u64;
|
||||||
|
assert!(c <= 1023, "invalid cylinder {}", c);
|
||||||
|
|
||||||
|
let mut data = ThunkData::new();
|
||||||
|
data.eax = 0x0200 | (dap.sectors as u32);
|
||||||
|
data.ebx = dap.buffer as u32;
|
||||||
|
data.ecx =
|
||||||
|
(s as u32) | (((c as u32) & 0xFF) << 8) | ((((c as u32) >> 8) & 0x3) << 6);
|
||||||
|
data.edx = (self.boot_disk as u32) | ((h as u32) << 8);
|
||||||
|
data.es = dap.segment;
|
||||||
|
|
||||||
|
data.with(self.thunk13);
|
||||||
|
|
||||||
|
//TODO: return result on error
|
||||||
|
let ah = ({ data.eax } >> 8) & 0xFF;
|
||||||
|
assert_eq!(ah, 0);
|
||||||
|
} else {
|
||||||
|
ptr::write(DISK_ADDRESS_PACKET_ADDR as *mut DiskAddressPacket, dap);
|
||||||
|
|
||||||
|
let mut data = ThunkData::new();
|
||||||
|
data.eax = 0x4200;
|
||||||
|
data.edx = self.boot_disk as u32;
|
||||||
|
data.esi = DISK_ADDRESS_PACKET_ADDR as u32;
|
||||||
|
|
||||||
|
data.with(self.thunk13);
|
||||||
|
|
||||||
|
//TODO: return result on error
|
||||||
|
let ah = ({ data.eax } >> 8) & 0xFF;
|
||||||
|
assert_eq!(ah, 0);
|
||||||
|
|
||||||
|
//TODO: check blocks transferred
|
||||||
|
// dap = ptr::read(DISK_ADDRESS_PACKET_ADDR as *mut DiskAddressPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr::copy(DISK_BIOS_ADDR as *const u8, chunk.as_mut_ptr(), chunk.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(buffer.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result<usize> {
|
||||||
|
log::error!(
|
||||||
|
"DiskBios::write_at(0x{:X}, 0x{:X}:0x{:X}) not allowed",
|
||||||
|
block,
|
||||||
|
buffer.as_ptr() as usize,
|
||||||
|
buffer.len()
|
||||||
|
);
|
||||||
|
Err(Error::new(EIO))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&mut self) -> Result<u64> {
|
||||||
|
log::error!("DiskBios::size not implemented");
|
||||||
|
Err(Error::new(EIO))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
/// Print to console
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! print {
|
||||||
|
($($arg:tt)*) => ({
|
||||||
|
use core::fmt::Write;
|
||||||
|
#[cfg(feature = "serial_debug")]
|
||||||
|
{
|
||||||
|
let _ = write!($crate::os::serial::COM1.lock(), $($arg)*);
|
||||||
|
}
|
||||||
|
let _ = write!($crate::os::VGA.lock(), $($arg)*);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print with new line to console
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! println {
|
||||||
|
() => (print!("\n"));
|
||||||
|
($fmt:expr) => (print!(concat!($fmt, "\n")));
|
||||||
|
($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
use core::{cmp, mem, ptr};
|
||||||
|
|
||||||
|
use crate::area_add;
|
||||||
|
use crate::os::{OsMemoryEntry, OsMemoryKind};
|
||||||
|
|
||||||
|
use super::{thunk::ThunkData, MEMORY_MAP_ADDR};
|
||||||
|
|
||||||
|
#[repr(C, packed)]
|
||||||
|
struct MemoryMapEntry {
|
||||||
|
pub base: u64,
|
||||||
|
pub size: u64,
|
||||||
|
pub kind: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MemoryMapIter {
|
||||||
|
thunk15: extern "C" fn(),
|
||||||
|
data: ThunkData,
|
||||||
|
first: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemoryMapIter {
|
||||||
|
pub fn new(thunk15: extern "C" fn()) -> Self {
|
||||||
|
Self {
|
||||||
|
thunk15,
|
||||||
|
data: ThunkData::new(),
|
||||||
|
first: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for MemoryMapIter {
|
||||||
|
type Item = OsMemoryEntry;
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.first {
|
||||||
|
self.first = false;
|
||||||
|
} else if self.data.ebx == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.data.eax = 0xE820;
|
||||||
|
self.data.ecx = mem::size_of::<MemoryMapEntry>() as u32;
|
||||||
|
self.data.edx = 0x534D4150;
|
||||||
|
self.data.edi = MEMORY_MAP_ADDR as u32;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
self.data.with(self.thunk15);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: return error?
|
||||||
|
assert_eq!({ self.data.eax }, 0x534D4150);
|
||||||
|
assert_eq!({ self.data.ecx }, mem::size_of::<MemoryMapEntry>() as u32);
|
||||||
|
|
||||||
|
let entry = unsafe { ptr::read(MEMORY_MAP_ADDR as *const MemoryMapEntry) };
|
||||||
|
Some(Self::Item {
|
||||||
|
base: entry.base,
|
||||||
|
size: entry.size,
|
||||||
|
kind: match entry.kind {
|
||||||
|
0 => OsMemoryKind::Null,
|
||||||
|
1 => OsMemoryKind::Free,
|
||||||
|
3 => OsMemoryKind::Reclaim,
|
||||||
|
_ => OsMemoryKind::Reserved,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn memory_map(thunk15: extern "C" fn()) -> Option<(usize, usize)> {
|
||||||
|
let mut heap_limits = None;
|
||||||
|
for entry in MemoryMapIter::new(thunk15) {
|
||||||
|
let heap_start = 1 * 1024 * 1024;
|
||||||
|
if { entry.kind } == OsMemoryKind::Free
|
||||||
|
&& entry.base <= heap_start as u64
|
||||||
|
&& (entry.base + entry.size) >= heap_start as u64
|
||||||
|
{
|
||||||
|
let heap_end = cmp::min(entry.base + entry.size, usize::MAX as u64) as usize;
|
||||||
|
if heap_end >= heap_start {
|
||||||
|
heap_limits = Some((heap_start, heap_end - heap_start));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
area_add(entry);
|
||||||
|
}
|
||||||
|
heap_limits
|
||||||
|
}
|
|
@ -0,0 +1,310 @@
|
||||||
|
use alloc::alloc::{alloc_zeroed, Layout};
|
||||||
|
use core::{convert::TryFrom, mem, ptr, slice};
|
||||||
|
use linked_list_allocator::LockedHeap;
|
||||||
|
use spin::Mutex;
|
||||||
|
|
||||||
|
use crate::logger::LOGGER;
|
||||||
|
use crate::os::{Os, OsHwDesc, OsKey, OsVideoMode};
|
||||||
|
use crate::KernelArgs;
|
||||||
|
|
||||||
|
use self::disk::DiskBios;
|
||||||
|
use self::memory_map::memory_map;
|
||||||
|
use self::thunk::ThunkData;
|
||||||
|
use self::vbe::VideoModeIter;
|
||||||
|
use self::vga::{Vga, VgaTextColor};
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod macros;
|
||||||
|
|
||||||
|
mod disk;
|
||||||
|
mod memory_map;
|
||||||
|
mod panic;
|
||||||
|
pub(crate) mod serial;
|
||||||
|
mod thunk;
|
||||||
|
mod vbe;
|
||||||
|
mod vga;
|
||||||
|
|
||||||
|
// Real mode memory allocation, for use with thunk
|
||||||
|
// 0x500 to 0x7BFF is free
|
||||||
|
const DISK_BIOS_ADDR: usize = 0x70000; // 64 KiB at 448 KiB, ends at 512 KiB
|
||||||
|
const VBE_CARD_INFO_ADDR: usize = 0x1000; // 512 bytes, ends at 0x11FF
|
||||||
|
const VBE_MODE_INFO_ADDR: usize = 0x1200; // 256 bytes, ends at 0x12FF
|
||||||
|
const VBE_EDID_ADDR: usize = 0x1300; // 128 bytes, ends at 0x137F
|
||||||
|
const MEMORY_MAP_ADDR: usize = 0x1380; // 24 bytes, ends at 0x1397
|
||||||
|
const DISK_ADDRESS_PACKET_ADDR: usize = 0x1398; // 16 bytes, ends at 0x13A7
|
||||||
|
const THUNK_STACK_ADDR: usize = 0x7C00; // Grows downwards
|
||||||
|
const VGA_ADDR: usize = 0xB8000;
|
||||||
|
|
||||||
|
#[global_allocator]
|
||||||
|
static ALLOCATOR: LockedHeap = LockedHeap::empty();
|
||||||
|
|
||||||
|
pub(crate) static VGA: Mutex<Vga> = Mutex::new(unsafe { Vga::new(VGA_ADDR, 80, 25) });
|
||||||
|
|
||||||
|
pub struct OsBios {
|
||||||
|
boot_disk: usize,
|
||||||
|
thunk10: extern "C" fn(),
|
||||||
|
thunk13: extern "C" fn(),
|
||||||
|
thunk15: extern "C" fn(),
|
||||||
|
thunk16: extern "C" fn(),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct Rsdp {
|
||||||
|
signature: [u8; 8],
|
||||||
|
checksum: u8,
|
||||||
|
oemid: [u8; 6],
|
||||||
|
revision: u8,
|
||||||
|
rsdt_address: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct Xsdp {
|
||||||
|
rsdp: Rsdp,
|
||||||
|
|
||||||
|
length: u32,
|
||||||
|
xsdt_address: u64,
|
||||||
|
extended_checksum: u8,
|
||||||
|
reserved: [u8; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn search_rsdp(start: usize, end: usize) -> Option<(u64, u64)> {
|
||||||
|
// Align start up to 16 bytes
|
||||||
|
let mut addr = ((start + 15) / 16) * 16;
|
||||||
|
// Search until reading the end of the Rsdp would be past the end of the memory area
|
||||||
|
while addr + mem::size_of::<Rsdp>() <= end {
|
||||||
|
let rsdp = ptr::read(addr as *const Rsdp);
|
||||||
|
if &rsdp.signature == b"RSD PTR " {
|
||||||
|
//TODO: check checksum?
|
||||||
|
if rsdp.revision == 0 {
|
||||||
|
return Some((addr as u64, mem::size_of::<Rsdp>() as u64));
|
||||||
|
} else if rsdp.revision == 2 {
|
||||||
|
let xsdp = ptr::read(addr as *const Xsdp);
|
||||||
|
//TODO: check extended checksum?
|
||||||
|
return Some((addr as u64, xsdp.length as u64));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rsdp is always aligned to 16 bytes
|
||||||
|
addr += 16;
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Os<DiskBios, VideoModeIter> for OsBios {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"x86/BIOS"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_zeroed_page_aligned(&self, size: usize) -> *mut u8 {
|
||||||
|
assert!(size != 0);
|
||||||
|
|
||||||
|
let page_size = self.page_size();
|
||||||
|
let pages = (size + page_size - 1) / page_size;
|
||||||
|
|
||||||
|
let ptr =
|
||||||
|
unsafe { alloc_zeroed(Layout::from_size_align(pages * page_size, page_size).unwrap()) };
|
||||||
|
|
||||||
|
assert!(!ptr.is_null());
|
||||||
|
ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
fn page_size(&self) -> usize {
|
||||||
|
4096
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filesystem(
|
||||||
|
&self,
|
||||||
|
password_opt: Option<&[u8]>,
|
||||||
|
) -> syscall::Result<redoxfs::FileSystem<DiskBios>> {
|
||||||
|
let disk = DiskBios::new(u8::try_from(self.boot_disk).unwrap(), self.thunk13);
|
||||||
|
|
||||||
|
//TODO: get block from partition table
|
||||||
|
let block = 2 * crate::MIBI as u64 / redoxfs::BLOCK_SIZE;
|
||||||
|
redoxfs::FileSystem::open(disk, password_opt, Some(block), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hwdesc(&self) -> OsHwDesc {
|
||||||
|
// See ACPI specification - Finding the RSDP on IA-PC Systems
|
||||||
|
unsafe {
|
||||||
|
let ebda_segment = ptr::read(0x40E as *const u16);
|
||||||
|
let ebda_addr = (ebda_segment as usize) << 4;
|
||||||
|
if let Some((addr, size)) =
|
||||||
|
search_rsdp(ebda_addr, ebda_addr + 1024).or(search_rsdp(0xE0000, 0xFFFFF))
|
||||||
|
{
|
||||||
|
// Copy to a page
|
||||||
|
let page_aligned = self.alloc_zeroed_page_aligned(size as usize);
|
||||||
|
ptr::copy(addr as *const u8, page_aligned, size as usize);
|
||||||
|
return OsHwDesc::Acpi(page_aligned as u64, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OsHwDesc::NotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
fn video_outputs(&self) -> usize {
|
||||||
|
//TODO: return 1 only if vbe supported?
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn video_modes(&self, _output_i: usize) -> VideoModeIter {
|
||||||
|
VideoModeIter::new(self.thunk10)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_video_mode(&self, _output_i: usize, mode: &mut OsVideoMode) {
|
||||||
|
// Set video mode
|
||||||
|
let mut data = ThunkData::new();
|
||||||
|
data.eax = 0x4F02;
|
||||||
|
data.ebx = mode.id;
|
||||||
|
unsafe {
|
||||||
|
data.with(self.thunk10);
|
||||||
|
}
|
||||||
|
//TODO: check result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn best_resolution(&self, _output_i: usize) -> Option<(u32, u32)> {
|
||||||
|
let mut data = ThunkData::new();
|
||||||
|
data.eax = 0x4F15;
|
||||||
|
data.ebx = 0x01;
|
||||||
|
data.ecx = 0;
|
||||||
|
data.edx = 0;
|
||||||
|
data.edi = VBE_EDID_ADDR as u32;
|
||||||
|
unsafe {
|
||||||
|
data.with(self.thunk10);
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.eax == 0x4F {
|
||||||
|
let edid = unsafe { slice::from_raw_parts(VBE_EDID_ADDR as *const u8, 128) };
|
||||||
|
|
||||||
|
Some((
|
||||||
|
(edid[0x38] as u32) | (((edid[0x3A] as u32) & 0xF0) << 4),
|
||||||
|
(edid[0x3B] as u32) | (((edid[0x3D] as u32) & 0xF0) << 4),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
log::warn!("Failed to get VBE EDID: 0x{:X}", { data.eax });
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_key(&self) -> OsKey {
|
||||||
|
// Read keypress
|
||||||
|
let mut data = ThunkData::new();
|
||||||
|
unsafe {
|
||||||
|
data.with(self.thunk16);
|
||||||
|
}
|
||||||
|
match (data.eax >> 8) as u8 {
|
||||||
|
0x4B => OsKey::Left,
|
||||||
|
0x4D => OsKey::Right,
|
||||||
|
0x48 => OsKey::Up,
|
||||||
|
0x50 => OsKey::Down,
|
||||||
|
0x0E => OsKey::Backspace,
|
||||||
|
0x53 => OsKey::Delete,
|
||||||
|
0x1C => OsKey::Enter,
|
||||||
|
_ => match data.eax as u8 {
|
||||||
|
0 => OsKey::Other,
|
||||||
|
b => OsKey::Char(b as char),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_text(&self) {
|
||||||
|
//TODO: clear screen for VGA
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_text_position(&self) -> (usize, usize) {
|
||||||
|
let vga = VGA.lock();
|
||||||
|
(vga.x, vga.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_text_position(&self, x: usize, y: usize) {
|
||||||
|
//TODO: ensure this is inside bounds!
|
||||||
|
let mut vga = VGA.lock();
|
||||||
|
vga.x = x;
|
||||||
|
vga.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_text_highlight(&self, highlight: bool) {
|
||||||
|
let mut vga = VGA.lock();
|
||||||
|
if highlight {
|
||||||
|
vga.bg = VgaTextColor::Gray;
|
||||||
|
vga.fg = VgaTextColor::Black;
|
||||||
|
} else {
|
||||||
|
vga.bg = VgaTextColor::Black;
|
||||||
|
vga.fg = VgaTextColor::Gray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn start(
|
||||||
|
kernel_entry: extern "C" fn(
|
||||||
|
page_table: usize,
|
||||||
|
stack: u64,
|
||||||
|
func: u64,
|
||||||
|
args: *const KernelArgs,
|
||||||
|
long_mode: usize,
|
||||||
|
) -> !,
|
||||||
|
boot_disk: usize,
|
||||||
|
thunk10: extern "C" fn(),
|
||||||
|
thunk13: extern "C" fn(),
|
||||||
|
thunk15: extern "C" fn(),
|
||||||
|
thunk16: extern "C" fn(),
|
||||||
|
) -> ! {
|
||||||
|
#[cfg(feature = "serial_debug")]
|
||||||
|
{
|
||||||
|
let mut com1 = serial::COM1.lock();
|
||||||
|
com1.init();
|
||||||
|
com1.write(b"SERIAL\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Make sure we are in mode 3 (80x25 text mode)
|
||||||
|
let mut data = ThunkData::new();
|
||||||
|
data.eax = 0x03;
|
||||||
|
data.with(thunk10);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Disable cursor
|
||||||
|
let mut data = ThunkData::new();
|
||||||
|
data.eax = 0x0100;
|
||||||
|
data.ecx = 0x3F00;
|
||||||
|
data.with(thunk10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear screen
|
||||||
|
VGA.lock().clear();
|
||||||
|
|
||||||
|
// Set logger
|
||||||
|
LOGGER.init();
|
||||||
|
|
||||||
|
let mut os = OsBios {
|
||||||
|
boot_disk,
|
||||||
|
thunk10,
|
||||||
|
thunk13,
|
||||||
|
thunk15,
|
||||||
|
thunk16,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (heap_start, heap_size) = memory_map(os.thunk15).expect("No memory for heap");
|
||||||
|
|
||||||
|
ALLOCATOR.lock().init(heap_start as *mut u8, heap_size);
|
||||||
|
|
||||||
|
let (page_phys, func, args) = crate::main(&mut os);
|
||||||
|
|
||||||
|
kernel_entry(
|
||||||
|
page_phys,
|
||||||
|
args.stack_base
|
||||||
|
+ args.stack_size
|
||||||
|
+ if crate::KERNEL_64BIT {
|
||||||
|
crate::arch::x64::PHYS_OFFSET as u64
|
||||||
|
} else {
|
||||||
|
crate::arch::x32::PHYS_OFFSET as u64
|
||||||
|
},
|
||||||
|
func,
|
||||||
|
&args,
|
||||||
|
if crate::KERNEL_64BIT { 1 } else { 0 },
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
//! Intrinsics for panic handling
|
||||||
|
|
||||||
|
use core::alloc::Layout;
|
||||||
|
use core::arch::asm;
|
||||||
|
use core::panic::PanicInfo;
|
||||||
|
|
||||||
|
#[lang = "eh_personality"]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn rust_eh_personality() {}
|
||||||
|
|
||||||
|
/// Required to handle panics
|
||||||
|
#[panic_handler]
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn rust_begin_unwind(info: &PanicInfo<'_>) -> ! {
|
||||||
|
unsafe {
|
||||||
|
println!("BOOTLOADER PANIC:\n{}", info);
|
||||||
|
loop {
|
||||||
|
asm!("hlt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[alloc_error_handler]
|
||||||
|
#[no_mangle]
|
||||||
|
#[allow(improper_ctypes_definitions)] // Layout is not repr(C)
|
||||||
|
pub extern "C" fn rust_oom(_layout: Layout) -> ! {
|
||||||
|
panic!("memory allocation failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[no_mangle]
|
||||||
|
/// Required to handle panics
|
||||||
|
pub extern "C" fn _Unwind_Resume() -> ! {
|
||||||
|
loop {
|
||||||
|
unsafe {
|
||||||
|
asm!("hlt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
use spin::Mutex;
|
||||||
|
use syscall::Pio;
|
||||||
|
|
||||||
|
use crate::serial_16550::SerialPort;
|
||||||
|
|
||||||
|
pub static COM1: Mutex<SerialPort<Pio<u8>>> = Mutex::new(SerialPort::<Pio<u8>>::new(0x3F8));
|
||||||
|
pub static COM2: Mutex<SerialPort<Pio<u8>>> = Mutex::new(SerialPort::<Pio<u8>>::new(0x2F8));
|
||||||
|
pub static COM3: Mutex<SerialPort<Pio<u8>>> = Mutex::new(SerialPort::<Pio<u8>>::new(0x3E8));
|
||||||
|
pub static COM4: Mutex<SerialPort<Pio<u8>>> = Mutex::new(SerialPort::<Pio<u8>>::new(0x2E8));
|
|
@ -0,0 +1,46 @@
|
||||||
|
use core::ptr;
|
||||||
|
|
||||||
|
use super::THUNK_STACK_ADDR;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct ThunkData {
|
||||||
|
pub es: u16,
|
||||||
|
pub edi: u32,
|
||||||
|
pub esi: u32,
|
||||||
|
pub ebp: u32,
|
||||||
|
pub ebx: u32,
|
||||||
|
pub edx: u32,
|
||||||
|
pub ecx: u32,
|
||||||
|
pub eax: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ThunkData {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
es: 0,
|
||||||
|
edi: 0,
|
||||||
|
esi: 0,
|
||||||
|
ebp: 0,
|
||||||
|
ebx: 0,
|
||||||
|
edx: 0,
|
||||||
|
ecx: 0,
|
||||||
|
eax: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn save(&self) {
|
||||||
|
ptr::write((THUNK_STACK_ADDR - 64) as *mut ThunkData, *self);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn load(&mut self) {
|
||||||
|
*self = ptr::read((THUNK_STACK_ADDR - 64) as *const ThunkData);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn with(&mut self, f: extern "C" fn()) {
|
||||||
|
self.save();
|
||||||
|
f();
|
||||||
|
self.load();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,151 @@
|
||||||
|
use core::ptr;
|
||||||
|
use log::error;
|
||||||
|
|
||||||
|
use crate::os::OsVideoMode;
|
||||||
|
|
||||||
|
use super::{ThunkData, VBE_CARD_INFO_ADDR, VBE_MODE_INFO_ADDR};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct VbeFarPtr {
|
||||||
|
pub offset: u16,
|
||||||
|
pub segment: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VbeFarPtr {
|
||||||
|
pub unsafe fn as_ptr<T>(&self) -> *const T {
|
||||||
|
(((self.segment as usize) << 4) + (self.offset as usize)) as *const T
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct VbeCardInfo {
|
||||||
|
pub signature: [u8; 4],
|
||||||
|
pub version: u16,
|
||||||
|
pub oemstring: VbeFarPtr,
|
||||||
|
pub capabilities: [u8; 4],
|
||||||
|
pub videomodeptr: VbeFarPtr,
|
||||||
|
pub totalmemory: u16,
|
||||||
|
pub oemsoftwarerev: u16,
|
||||||
|
pub oemvendornameptr: VbeFarPtr,
|
||||||
|
pub oemproductnameptr: VbeFarPtr,
|
||||||
|
pub oemproductrevptr: VbeFarPtr,
|
||||||
|
pub reserved: [u8; 222],
|
||||||
|
pub oemdata: [u8; 256],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct VbeModeInfo {
|
||||||
|
pub attributes: u16,
|
||||||
|
pub win_a: u8,
|
||||||
|
pub win_b: u8,
|
||||||
|
pub granularity: u16,
|
||||||
|
pub winsize: u16,
|
||||||
|
pub segment_a: u16,
|
||||||
|
pub segment_b: u16,
|
||||||
|
pub winfuncptr: u32,
|
||||||
|
pub bytesperscanline: u16,
|
||||||
|
pub xresolution: u16,
|
||||||
|
pub yresolution: u16,
|
||||||
|
pub xcharsize: u8,
|
||||||
|
pub ycharsize: u8,
|
||||||
|
pub numberofplanes: u8,
|
||||||
|
pub bitsperpixel: u8,
|
||||||
|
pub numberofbanks: u8,
|
||||||
|
pub memorymodel: u8,
|
||||||
|
pub banksize: u8,
|
||||||
|
pub numberofimagepages: u8,
|
||||||
|
pub unused: u8,
|
||||||
|
pub redmasksize: u8,
|
||||||
|
pub redfieldposition: u8,
|
||||||
|
pub greenmasksize: u8,
|
||||||
|
pub greenfieldposition: u8,
|
||||||
|
pub bluemasksize: u8,
|
||||||
|
pub bluefieldposition: u8,
|
||||||
|
pub rsvdmasksize: u8,
|
||||||
|
pub rsvdfieldposition: u8,
|
||||||
|
pub directcolormodeinfo: u8,
|
||||||
|
pub physbaseptr: u32,
|
||||||
|
pub offscreenmemoryoffset: u32,
|
||||||
|
pub offscreenmemsize: u16,
|
||||||
|
pub reserved: [u8; 206],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct VideoModeIter {
|
||||||
|
thunk10: extern "C" fn(),
|
||||||
|
mode_ptr: *const u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VideoModeIter {
|
||||||
|
pub fn new(thunk10: extern "C" fn()) -> Self {
|
||||||
|
// Get card info
|
||||||
|
let mut data = ThunkData::new();
|
||||||
|
data.eax = 0x4F00;
|
||||||
|
data.edi = VBE_CARD_INFO_ADDR as u32;
|
||||||
|
unsafe {
|
||||||
|
data.with(thunk10);
|
||||||
|
}
|
||||||
|
let mode_ptr = if data.eax == 0x004F {
|
||||||
|
let card_info = unsafe { ptr::read(VBE_CARD_INFO_ADDR as *const VbeCardInfo) };
|
||||||
|
unsafe { card_info.videomodeptr.as_ptr::<u16>() }
|
||||||
|
} else {
|
||||||
|
error!("Failed to read VBE card info: 0x{:04X}", { data.eax });
|
||||||
|
ptr::null()
|
||||||
|
};
|
||||||
|
Self { thunk10, mode_ptr }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for VideoModeIter {
|
||||||
|
type Item = OsVideoMode;
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.mode_ptr.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// Set bit 14 to get linear frame buffer
|
||||||
|
let mode = unsafe { *self.mode_ptr } | (1 << 14);
|
||||||
|
if mode == 0xFFFF {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
self.mode_ptr = unsafe { self.mode_ptr.add(1) };
|
||||||
|
|
||||||
|
// Get mode info
|
||||||
|
let mut data = ThunkData::new();
|
||||||
|
data.eax = 0x4F01;
|
||||||
|
data.ecx = mode as u32;
|
||||||
|
data.edi = VBE_MODE_INFO_ADDR as u32;
|
||||||
|
unsafe {
|
||||||
|
data.with(self.thunk10);
|
||||||
|
}
|
||||||
|
if data.eax == 0x004F {
|
||||||
|
let mode_info = unsafe { ptr::read(VBE_MODE_INFO_ADDR as *const VbeModeInfo) };
|
||||||
|
|
||||||
|
// We only support 32-bits per pixel modes
|
||||||
|
if mode_info.bitsperpixel != 32 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let width = mode_info.xresolution as u32;
|
||||||
|
let height = mode_info.yresolution as u32;
|
||||||
|
//TODO: support stride that is not a multiple of 4
|
||||||
|
let stride = mode_info.bytesperscanline as u32 / 4;
|
||||||
|
|
||||||
|
return Some(OsVideoMode {
|
||||||
|
id: mode as u32,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
stride,
|
||||||
|
base: mode_info.physbaseptr as u64,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
error!("Failed to read VBE mode 0x{:04X} info: 0x{:04X}", mode, {
|
||||||
|
data.eax
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
use core::{fmt, slice};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct VgaTextBlock {
|
||||||
|
pub char: u8,
|
||||||
|
pub color: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum VgaTextColor {
|
||||||
|
Black = 0,
|
||||||
|
Blue = 1,
|
||||||
|
Green = 2,
|
||||||
|
Cyan = 3,
|
||||||
|
Red = 4,
|
||||||
|
Purple = 5,
|
||||||
|
Brown = 6,
|
||||||
|
Gray = 7,
|
||||||
|
DarkGray = 8,
|
||||||
|
LightBlue = 9,
|
||||||
|
LightGreen = 10,
|
||||||
|
LightCyan = 11,
|
||||||
|
LightRed = 12,
|
||||||
|
LightPurple = 13,
|
||||||
|
Yellow = 14,
|
||||||
|
White = 15,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Vga {
|
||||||
|
pub base: usize,
|
||||||
|
pub width: usize,
|
||||||
|
pub height: usize,
|
||||||
|
pub x: usize,
|
||||||
|
pub y: usize,
|
||||||
|
pub bg: VgaTextColor,
|
||||||
|
pub fg: VgaTextColor,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vga {
|
||||||
|
pub const unsafe fn new(base: usize, width: usize, height: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
base,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
bg: VgaTextColor::Black,
|
||||||
|
fg: VgaTextColor::Gray,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn blocks(&mut self) -> &'static mut [VgaTextBlock] {
|
||||||
|
slice::from_raw_parts_mut(self.base as *mut VgaTextBlock, self.width * self.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.x = 0;
|
||||||
|
self.y = 0;
|
||||||
|
let blocks = unsafe { self.blocks() };
|
||||||
|
for i in 0..blocks.len() {
|
||||||
|
blocks[i] = VgaTextBlock {
|
||||||
|
char: 0,
|
||||||
|
color: ((self.bg as u8) << 4) | (self.fg as u8),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Write for Vga {
|
||||||
|
fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
|
||||||
|
let blocks = unsafe { self.blocks() };
|
||||||
|
for c in s.chars() {
|
||||||
|
if self.x >= self.width {
|
||||||
|
self.x = 0;
|
||||||
|
self.y += 1;
|
||||||
|
}
|
||||||
|
while self.y >= self.height {
|
||||||
|
for y in 1..self.height {
|
||||||
|
for x in 0..self.width {
|
||||||
|
let i = y * self.width + x;
|
||||||
|
let j = i - self.width;
|
||||||
|
blocks[j] = blocks[i];
|
||||||
|
if y + 1 == self.height {
|
||||||
|
blocks[i].char = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.y -= 1;
|
||||||
|
}
|
||||||
|
match c {
|
||||||
|
'\x08' => {
|
||||||
|
if self.x > 0 {
|
||||||
|
self.x -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'\r' => {
|
||||||
|
self.x = 0;
|
||||||
|
}
|
||||||
|
'\n' => {
|
||||||
|
self.x = 0;
|
||||||
|
self.y += 1;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let i = self.y * self.width + self.x;
|
||||||
|
if let Some(block) = blocks.get_mut(i) {
|
||||||
|
block.char = c as u8;
|
||||||
|
block.color = ((self.bg as u8) << 4) | (self.fg as u8);
|
||||||
|
}
|
||||||
|
self.x += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
use redoxfs::Disk;
|
||||||
|
|
||||||
|
#[cfg(all(target_arch = "x86", target_os = "none"))]
|
||||||
|
pub use self::bios::*;
|
||||||
|
|
||||||
|
#[cfg(all(target_arch = "x86", target_os = "none"))]
|
||||||
|
#[macro_use]
|
||||||
|
mod bios;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum OsHwDesc {
|
||||||
|
Acpi(u64, u64),
|
||||||
|
DeviceTree(u64, u64),
|
||||||
|
NotFound,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum OsKey {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Backspace,
|
||||||
|
Delete,
|
||||||
|
Enter,
|
||||||
|
Char(char),
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep synced with BootloaderMemoryKind in kernel
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
#[repr(u64)]
|
||||||
|
pub enum OsMemoryKind {
|
||||||
|
Null = 0,
|
||||||
|
Free = 1,
|
||||||
|
Reclaim = 2,
|
||||||
|
Reserved = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep synced with BootloaderMemoryEntry in kernel
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
#[repr(C, packed(8))]
|
||||||
|
pub struct OsMemoryEntry {
|
||||||
|
pub base: u64,
|
||||||
|
pub size: u64,
|
||||||
|
pub kind: OsMemoryKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct OsVideoMode {
|
||||||
|
pub id: u32,
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
pub stride: u32,
|
||||||
|
pub base: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Os<D: Disk, V: Iterator<Item = OsVideoMode>> {
|
||||||
|
fn name(&self) -> &str;
|
||||||
|
|
||||||
|
fn alloc_zeroed_page_aligned(&self, size: usize) -> *mut u8;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn page_size(&self) -> usize;
|
||||||
|
|
||||||
|
fn filesystem(&self, password_opt: Option<&[u8]>) -> syscall::Result<redoxfs::FileSystem<D>>;
|
||||||
|
|
||||||
|
fn hwdesc(&self) -> OsHwDesc;
|
||||||
|
|
||||||
|
fn video_outputs(&self) -> usize;
|
||||||
|
fn video_modes(&self, output_i: usize) -> V;
|
||||||
|
fn set_video_mode(&self, output_i: usize, mode: &mut OsVideoMode);
|
||||||
|
fn best_resolution(&self, output_i: usize) -> Option<(u32, u32)>;
|
||||||
|
|
||||||
|
fn get_key(&self) -> OsKey;
|
||||||
|
|
||||||
|
fn clear_text(&self);
|
||||||
|
fn get_text_position(&self) -> (usize, usize);
|
||||||
|
fn set_text_position(&self, x: usize, y: usize);
|
||||||
|
fn set_text_highlight(&self, highlight: bool);
|
||||||
|
}
|
|
@ -0,0 +1,142 @@
|
||||||
|
use bitflags::bitflags;
|
||||||
|
use core::convert::TryInto;
|
||||||
|
use core::fmt;
|
||||||
|
use core::ptr::{addr_of, addr_of_mut};
|
||||||
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||||
|
use syscall::io::Pio;
|
||||||
|
use syscall::io::{Io, Mmio, ReadOnly};
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
/// Interrupt enable flags
|
||||||
|
struct IntEnFlags: u8 {
|
||||||
|
const RECEIVED = 1;
|
||||||
|
const SENT = 1 << 1;
|
||||||
|
const ERRORED = 1 << 2;
|
||||||
|
const STATUS_CHANGE = 1 << 3;
|
||||||
|
// 4 to 7 are unused
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
/// Line status flags
|
||||||
|
struct LineStsFlags: u8 {
|
||||||
|
const INPUT_FULL = 1;
|
||||||
|
// 1 to 4 unknown
|
||||||
|
const OUTPUT_EMPTY = 1 << 5;
|
||||||
|
// 6 and 7 unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct SerialPort<T: Io> {
|
||||||
|
/// Data register, read to receive, write to send
|
||||||
|
data: T,
|
||||||
|
/// Interrupt enable
|
||||||
|
int_en: T,
|
||||||
|
/// FIFO control
|
||||||
|
fifo_ctrl: T,
|
||||||
|
/// Line control
|
||||||
|
line_ctrl: T,
|
||||||
|
/// Modem control
|
||||||
|
modem_ctrl: T,
|
||||||
|
/// Line status
|
||||||
|
line_sts: ReadOnly<T>,
|
||||||
|
/// Modem status
|
||||||
|
modem_sts: ReadOnly<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||||
|
impl SerialPort<Pio<u8>> {
|
||||||
|
pub const fn new(base: u16) -> SerialPort<Pio<u8>> {
|
||||||
|
SerialPort {
|
||||||
|
data: Pio::new(base),
|
||||||
|
int_en: Pio::new(base + 1),
|
||||||
|
fifo_ctrl: Pio::new(base + 2),
|
||||||
|
line_ctrl: Pio::new(base + 3),
|
||||||
|
modem_ctrl: Pio::new(base + 4),
|
||||||
|
line_sts: ReadOnly::new(Pio::new(base + 5)),
|
||||||
|
modem_sts: ReadOnly::new(Pio::new(base + 6)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SerialPort<Mmio<u32>> {
|
||||||
|
pub unsafe fn new(base: usize) -> &'static mut SerialPort<Mmio<u32>> {
|
||||||
|
&mut *(base as *mut Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Io> SerialPort<T>
|
||||||
|
where
|
||||||
|
T::Value: From<u8> + TryInto<u8>,
|
||||||
|
{
|
||||||
|
pub fn init(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
//TODO: Cleanup
|
||||||
|
// FIXME: Fix UB if unaligned
|
||||||
|
(&mut *addr_of_mut!(self.int_en)).write(0x00.into());
|
||||||
|
(&mut *addr_of_mut!(self.line_ctrl)).write(0x80.into());
|
||||||
|
(&mut *addr_of_mut!(self.data)).write(0x01.into());
|
||||||
|
(&mut *addr_of_mut!(self.int_en)).write(0x00.into());
|
||||||
|
(&mut *addr_of_mut!(self.line_ctrl)).write(0x03.into());
|
||||||
|
(&mut *addr_of_mut!(self.fifo_ctrl)).write(0xC7.into());
|
||||||
|
(&mut *addr_of_mut!(self.modem_ctrl)).write(0x0B.into());
|
||||||
|
(&mut *addr_of_mut!(self.int_en)).write(0x01.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn line_sts(&self) -> LineStsFlags {
|
||||||
|
LineStsFlags::from_bits_truncate(
|
||||||
|
(unsafe { &*addr_of!(self.line_sts) }.read() & 0xFF.into())
|
||||||
|
.try_into()
|
||||||
|
.unwrap_or(0),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn receive(&mut self) -> Option<u8> {
|
||||||
|
if self.line_sts().contains(LineStsFlags::INPUT_FULL) {
|
||||||
|
Some(
|
||||||
|
(unsafe { &*addr_of!(self.data) }.read() & 0xFF.into())
|
||||||
|
.try_into()
|
||||||
|
.unwrap_or(0),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send(&mut self, data: u8) {
|
||||||
|
while !self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY) {}
|
||||||
|
unsafe { &mut *addr_of_mut!(self.data) }.write(data.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&mut self, buf: &[u8]) {
|
||||||
|
for &b in buf {
|
||||||
|
match b {
|
||||||
|
8 | 0x7F => {
|
||||||
|
self.send(8);
|
||||||
|
self.send(b' ');
|
||||||
|
self.send(8);
|
||||||
|
}
|
||||||
|
b'\n' => {
|
||||||
|
self.send(b'\r');
|
||||||
|
self.send(b'\n');
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.send(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Io> fmt::Write for SerialPort<T>
|
||||||
|
where
|
||||||
|
T::Value: From<u8> + TryInto<u8>,
|
||||||
|
{
|
||||||
|
fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
|
||||||
|
self.write(s.as_bytes());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"llvm-target": "i686-unknown-none",
|
||||||
|
"target-endian": "little",
|
||||||
|
"target-pointer-width": "32",
|
||||||
|
"target-c-int-width": "32",
|
||||||
|
"data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128",
|
||||||
|
"arch": "x86",
|
||||||
|
"os": "none",
|
||||||
|
"env": "",
|
||||||
|
"vendor": "unknown",
|
||||||
|
"linker-flavor": "gcc",
|
||||||
|
"panic-strategy": "abort",
|
||||||
|
"pre-link-args": {
|
||||||
|
"gcc": ["-m32", "-nostdlib", "-static"]
|
||||||
|
},
|
||||||
|
"features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float",
|
||||||
|
"dynamic-linking": false,
|
||||||
|
"executables": false,
|
||||||
|
"relocation-model": "static",
|
||||||
|
"code-model": "large",
|
||||||
|
"disable-redzone": true,
|
||||||
|
"frame-pointer": "always",
|
||||||
|
"exe-suffix": "",
|
||||||
|
"has-rpath": false,
|
||||||
|
"no-default-libraries": true,
|
||||||
|
"position-independent-executables": false,
|
||||||
|
"tls-model": "global-dynamic"
|
||||||
|
}
|
Loading…
Reference in New Issue