use std::collections::HashMap;
use std::fmt;
#[derive(Debug, Clone)]
pub struct X86Embedded {
pub freestanding: X86FreestandingEnv,
pub target: X86BareMetalTarget,
pub boot_loader: X86BootLoader,
pub linker_script: X86LinkerScript,
pub intrinsics: X86IntrinsicsEmbedded,
pub interrupts: X86InterruptHandlers,
pub paging: X86PagingSupport,
pub firmware: X86FirmwareSupport,
pub compiler_flags: Vec<String>,
pub linker_flags: Vec<String>,
pub opt_level: String,
pub debug_info: bool,
}
impl X86Embedded {
pub fn new() -> Self {
Self {
freestanding: X86FreestandingEnv::default(),
target: X86BareMetalTarget::default(),
boot_loader: X86BootLoader::default(),
linker_script: X86LinkerScript::default(),
intrinsics: X86IntrinsicsEmbedded::default(),
interrupts: X86InterruptHandlers::default(),
paging: X86PagingSupport::default(),
firmware: X86FirmwareSupport::default(),
compiler_flags: Vec::new(),
linker_flags: Vec::new(),
opt_level: "2".into(),
debug_info: false,
}
}
pub fn new_32bit() -> Self {
let mut e = Self::new();
e.target.bitness = X86Bitness::Bits32;
e.linker_script.bitness = X86Bitness::Bits32;
e.paging.bitness = X86Bitness::Bits32;
e.compiler_flags.push("-m32".into());
e
}
pub fn new_64bit() -> Self {
let mut e = Self::new();
e.target.bitness = X86Bitness::Bits64;
e.linker_script.bitness = X86Bitness::Bits64;
e.paging.bitness = X86Bitness::Bits64;
e.compiler_flags.push("-m64".into());
e
}
pub fn generate_compiler_flags(&self) -> Vec<String> {
let mut flags = self.freestanding.generate_flags();
flags.extend(self.target.generate_flags());
flags.extend(self.compiler_flags.clone());
flags.push(format!("-O{}", self.opt_level));
if self.debug_info {
flags.push("-g".into());
}
flags
}
pub fn generate_linker_flags(&self) -> Vec<String> {
let mut flags = Vec::new();
flags.push(format!(
"-T{}",
self.linker_script
.filename
.clone()
.unwrap_or_else(|| "linker.ld".into())
));
flags.extend(self.linker_flags.clone());
flags
}
pub fn generate_all(&self) -> X86EmbeddedArtifacts {
X86EmbeddedArtifacts {
compiler_flags: self.generate_compiler_flags(),
linker_flags: self.generate_linker_flags(),
linker_script: self.linker_script.generate(),
boot_sector: self.boot_loader.generate_boot_sector(),
startup_asm: self.generate_startup_asm(),
idt: self.interrupts.generate_idt_asm(),
page_tables: self.paging.generate_page_tables_asm(),
crt0_source: self.generate_crt0(),
}
}
pub fn generate_startup_asm(&self) -> String {
let mut asm = String::new();
asm.push_str("/* X86 Embedded Startup Code — generated by llvm-native */\n");
asm.push_str(" .section .text._start\n");
asm.push_str(" .global _start\n");
asm.push_str(" .type _start, @function\n");
asm.push_str("_start:\n");
if self.target.bitness == X86Bitness::Bits64 {
asm.push_str(" /* Clear direction flag */\n");
asm.push_str(" cld\n");
asm.push_str(" /* Clear the frame pointer */\n");
asm.push_str(" xorq %rbp, %rbp\n");
asm.push_str(" /* Set up stack */\n");
asm.push_str(" lea stack_top(%rip), %rsp\n");
} else {
asm.push_str(" /* Clear direction flag */\n");
asm.push_str(" cld\n");
asm.push_str(" /* Clear the frame pointer */\n");
asm.push_str(" xorl %ebp, %ebp\n");
asm.push_str(" /* Set up stack */\n");
asm.push_str(" movl $stack_top, %esp\n");
}
asm.push_str(" /* Zero BSS */\n");
asm.push_str(" call zero_bss\n");
asm.push_str(" /* Initialize data sections if needed */\n");
asm.push_str(" call init_data\n");
if self.interrupts.idt_enabled {
asm.push_str(" /* Install IDT */\n");
asm.push_str(" call install_idt\n");
}
if self.paging.enabled {
asm.push_str(" /* Enable paging */\n");
asm.push_str(" call enable_paging\n");
}
asm.push_str(" /* Call main */\n");
if self.target.bitness == X86Bitness::Bits64 {
asm.push_str(" xorq %rdi, %rdi /* argc = 0 */\n");
asm.push_str(" xorq %rsi, %rsi /* argv = NULL */\n");
asm.push_str(" call kernel_main\n");
} else {
asm.push_str(" pushl $0 /* argv */\n");
asm.push_str(" pushl $0 /* argc */\n");
asm.push_str(" call kernel_main\n");
asm.push_str(" addl $8, %esp\n");
}
asm.push_str(" /* Halt if main returns */\n");
asm.push_str(" cli\n");
asm.push_str(" hlt\n");
asm.push_str("1: jmp 1b\n");
asm
}
pub fn generate_crt0(&self) -> String {
let mut c = String::new();
c.push_str("/* CRT0 — X86 Embedded C Runtime Startup */\n");
c.push_str("#include <stddef.h>\n");
c.push_str("#include <stdint.h>\n\n");
c.push_str("extern char __bss_start[];\n");
c.push_str("extern char __bss_end[];\n");
c.push_str("extern char __data_load_start[];\n");
c.push_str("extern char __data_start[];\n");
c.push_str("extern char __data_end[];\n\n");
c.push_str("void zero_bss(void) {\n");
c.push_str(" for (char *p = __bss_start; p < __bss_end; p++) {\n");
c.push_str(" *p = 0;\n");
c.push_str(" }\n");
c.push_str("}\n\n");
c.push_str("void init_data(void) {\n");
c.push_str(" size_t size = (size_t)(__data_end - __data_start);\n");
c.push_str(" for (size_t i = 0; i < size; i++) {\n");
c.push_str(" __data_start[i] = __data_load_start[i];\n");
c.push_str(" }\n");
c.push_str("}\n");
c
}
pub fn with_opt_level(mut self, level: &str) -> Self {
self.opt_level = level.to_string();
self
}
pub fn with_debug_info(mut self, enabled: bool) -> Self {
self.debug_info = enabled;
self
}
pub fn add_flag(mut self, flag: &str) -> Self {
self.compiler_flags.push(flag.to_string());
self
}
pub fn add_linker_flag(mut self, flag: &str) -> Self {
self.linker_flags.push(flag.to_string());
self
}
}
impl Default for X86Embedded {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86EmbeddedArtifacts {
pub compiler_flags: Vec<String>,
pub linker_flags: Vec<String>,
pub linker_script: String,
pub boot_sector: Option<Vec<u8>>,
pub startup_asm: String,
pub idt: Option<String>,
pub page_tables: Option<String>,
pub crt0_source: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86Bitness {
Bits16,
Bits32,
Bits64,
}
impl X86Bitness {
pub fn pointer_width(&self) -> u32 {
match self {
Self::Bits16 => 2,
Self::Bits32 => 4,
Self::Bits64 => 8,
}
}
pub fn march_flag(&self) -> &'static str {
match self {
Self::Bits16 => "-m16",
Self::Bits32 => "-m32",
Self::Bits64 => "-m64",
}
}
pub fn triple_suffix(&self) -> &'static str {
match self {
Self::Bits16 => "i386",
Self::Bits32 => "i386",
Self::Bits64 => "x86_64",
}
}
pub fn is_long_mode(&self) -> bool {
matches!(self, Self::Bits64)
}
pub fn is_protected_mode(&self) -> bool {
matches!(self, Self::Bits32)
}
pub fn is_real_mode(&self) -> bool {
matches!(self, Self::Bits16)
}
}
impl fmt::Display for X86Bitness {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Bits16 => write!(f, "16-bit"),
Self::Bits32 => write!(f, "32-bit"),
Self::Bits64 => write!(f, "64-bit"),
}
}
}
#[derive(Debug, Clone)]
pub struct X86FreestandingEnv {
pub freestanding: bool,
pub nostdlib: bool,
pub nostdinc: bool,
pub nostdincxx: bool,
pub nodefaultlibs: bool,
pub nostartfiles: bool,
pub nolibc: bool,
pub generate_crt0: bool,
pub stack_size: u64,
pub heap_size: u64,
pub defines: Vec<(String, Option<String>)>,
pub extra_flags: Vec<String>,
pub include_paths: Vec<String>,
}
impl X86FreestandingEnv {
pub fn new() -> Self {
Self {
freestanding: true,
nostdlib: true,
nostdinc: true,
nostdincxx: true,
nodefaultlibs: true,
nostartfiles: true,
nolibc: true,
generate_crt0: true,
stack_size: 65536, heap_size: 1048576, defines: vec![
("__STDC_HOSTED__".into(), Some("0".into())),
("__FREESTANDING__".into(), None),
("__X86_EMBEDDED__".into(), None),
],
extra_flags: Vec::new(),
include_paths: Vec::new(),
}
}
pub fn for_kernel() -> Self {
let mut env = Self::new();
env.defines.push(("__KERNEL__".into(), None));
env.defines.push(("__X86_KERNEL__".into(), None));
env.nolibc = true;
env.nostartfiles = true;
env
}
pub fn for_uefi() -> Self {
let mut env = Self::new();
env.defines.push(("EFI".into(), None));
env.defines.push(("UEFI".into(), None));
env.defines.push(("__UEFI_APPLICATION__".into(), None));
env.nostdlib = true;
env.nostartfiles = true;
env
}
pub fn generate_flags(&self) -> Vec<String> {
let mut flags = Vec::new();
if self.freestanding {
flags.push("-ffreestanding".into());
}
if self.nostdlib {
flags.push("-nostdlib".into());
}
if self.nostdinc {
flags.push("-nostdinc".into());
}
if self.nostdincxx {
flags.push("-nostdinc++".into());
}
if self.nodefaultlibs {
flags.push("-nodefaultlibs".into());
}
if self.nostartfiles {
flags.push("-nostartfiles".into());
}
if self.nolibc {
flags.push("-nolibc".into());
}
for (name, value) in &self.defines {
match value {
Some(v) => flags.push(format!("-D{}={}", name, v)),
None => flags.push(format!("-D{}", name)),
}
}
for inc in &self.include_paths {
flags.push(format!("-I{}", inc));
}
flags.extend(self.extra_flags.clone());
flags
}
pub fn crt0_defines(&self) -> Vec<String> {
let mut defs = Vec::new();
defs.push(format!("-DSTACK_SIZE={}", self.stack_size));
defs.push(format!("-DHEAP_SIZE={}", self.heap_size));
if self.freestanding {
defs.push("-DFREESTANDING".into());
}
defs
}
pub fn with_stack_size(mut self, bytes: u64) -> Self {
self.stack_size = bytes;
self
}
pub fn with_heap_size(mut self, bytes: u64) -> Self {
self.heap_size = bytes;
self
}
pub fn with_define(mut self, name: &str, value: Option<&str>) -> Self {
self.defines
.push((name.to_string(), value.map(|s| s.to_string())));
self
}
pub fn with_include(mut self, path: &str) -> Self {
self.include_paths.push(path.to_string());
self
}
pub fn with_flag(mut self, flag: &str) -> Self {
self.extra_flags.push(flag.to_string());
self
}
pub fn is_fully_freestanding(&self) -> bool {
self.freestanding && self.nostdlib && self.nostdinc
}
pub fn is_no_libc(&self) -> bool {
self.nolibc && self.nostdlib
}
pub fn target_triple(&self, bitness: X86Bitness) -> String {
match bitness {
X86Bitness::Bits16 => "i386-unknown-none".into(),
X86Bitness::Bits32 => "i386-unknown-none-elf".into(),
X86Bitness::Bits64 => "x86_64-unknown-none-elf".into(),
}
}
}
impl Default for X86FreestandingEnv {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86BareMetalTargetKind {
BareMetal,
BiosBoot,
UefiBoot,
Multiboot,
Multiboot2,
Coreboot,
LinuxKernelModule,
EfiByteCode,
Custom,
}
impl X86BareMetalTargetKind {
pub fn as_str(&self) -> &'static str {
match self {
Self::BareMetal => "bare-metal",
Self::BiosBoot => "bios-boot",
Self::UefiBoot => "uefi-boot",
Self::Multiboot => "multiboot",
Self::Multiboot2 => "multiboot2",
Self::Coreboot => "coreboot",
Self::LinuxKernelModule => "linux-kernel-module",
Self::EfiByteCode => "efi-byte-code",
Self::Custom => "custom",
}
}
pub fn default_output_format(&self) -> &'static str {
match self {
Self::BareMetal | Self::Coreboot | Self::Custom => "elf",
Self::BiosBoot => "binary",
Self::UefiBoot => "pe",
Self::Multiboot | Self::Multiboot2 => "elf",
Self::LinuxKernelModule => "elf",
Self::EfiByteCode => "ebc",
}
}
pub fn requires_boot_loader(&self) -> bool {
matches!(
self,
Self::BiosBoot | Self::UefiBoot | Self::Multiboot | Self::Multiboot2 | Self::Coreboot
)
}
pub fn has_firmware(&self) -> bool {
matches!(
self,
Self::BiosBoot | Self::UefiBoot | Self::Multiboot | Self::Multiboot2
)
}
pub fn is_kernel_module(&self) -> bool {
matches!(self, Self::LinuxKernelModule)
}
pub fn entry_point(&self) -> &'static str {
match self {
Self::BareMetal | Self::Custom => "_start",
Self::BiosBoot => "_start16",
Self::UefiBoot => "efi_main",
Self::Multiboot | Self::Multiboot2 => "_start",
Self::Coreboot => "_start",
Self::LinuxKernelModule => "init_module",
Self::EfiByteCode => "EbcMain",
}
}
}
impl fmt::Display for X86BareMetalTargetKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone)]
pub struct X86BareMetalTarget {
pub kind: X86BareMetalTargetKind,
pub bitness: X86Bitness,
pub triple: String,
pub cpu_features: Vec<String>,
pub cpu_model: String,
pub output_format: String,
pub entry_point: String,
pub pe_subsystem: Option<String>,
pub image_base: u64,
pub section_alignment: u64,
pub file_alignment: u64,
pub stack_reserve: u64,
pub stack_commit: u64,
pub heap_reserve: u64,
pub heap_commit: u64,
pub extra_flags: Vec<String>,
}
impl X86BareMetalTarget {
pub fn new() -> Self {
Self {
kind: X86BareMetalTargetKind::BareMetal,
bitness: X86Bitness::Bits64,
triple: "x86_64-unknown-none-elf".into(),
cpu_features: vec!["+mmx".into(), "+sse".into(), "+sse2".into()],
cpu_model: "x86-64".into(),
output_format: "elf".into(),
entry_point: "_start".into(),
pe_subsystem: None,
image_base: 0x1000000,
section_alignment: 0x1000,
file_alignment: 0x200,
stack_reserve: 0x100000,
stack_commit: 0x1000,
heap_reserve: 0x100000,
heap_commit: 0x1000,
extra_flags: Vec::new(),
}
}
pub fn bios_boot() -> Self {
Self {
kind: X86BareMetalTargetKind::BiosBoot,
bitness: X86Bitness::Bits16,
triple: "i386-unknown-none".into(),
cpu_features: vec![],
cpu_model: "i386".into(),
output_format: "binary".into(),
entry_point: "_start16".into(),
pe_subsystem: None,
image_base: 0x7C00,
section_alignment: 0x200,
file_alignment: 0x200,
stack_reserve: 0x1000,
stack_commit: 0x1000,
heap_reserve: 0,
heap_commit: 0,
extra_flags: vec!["-m16".into(), "-ffreestanding".into(), "-nostdlib".into()],
}
}
pub fn uefi_boot_64() -> Self {
Self {
kind: X86BareMetalTargetKind::UefiBoot,
bitness: X86Bitness::Bits64,
triple: "x86_64-unknown-windows-msvc".into(),
cpu_features: vec!["+mmx".into(), "+sse".into(), "+sse2".into()],
cpu_model: "x86-64".into(),
output_format: "pe".into(),
entry_point: "efi_main".into(),
pe_subsystem: Some("EFI_APPLICATION".into()),
image_base: 0x0,
section_alignment: 0x1000,
file_alignment: 0x200,
stack_reserve: 0x100000,
stack_commit: 0x1000,
heap_reserve: 0x100000,
heap_commit: 0x1000,
extra_flags: vec![
"-ffreestanding".into(),
"-nostdlib".into(),
"-Wl,--subsystem,efi_application".into(),
],
}
}
pub fn uefi_boot_32() -> Self {
let mut t = Self::uefi_boot_64();
t.bitness = X86Bitness::Bits32;
t.triple = "i386-unknown-windows-msvc".into();
t.cpu_model = "i686".into();
t.extra_flags.push("-m32".into());
t
}
pub fn multiboot() -> Self {
Self {
kind: X86BareMetalTargetKind::Multiboot,
bitness: X86Bitness::Bits32,
triple: "i386-unknown-none-elf".into(),
cpu_features: vec!["+mmx".into(), "+sse".into(), "+sse2".into()],
cpu_model: "i686".into(),
output_format: "elf".into(),
entry_point: "_start".into(),
pe_subsystem: None,
image_base: 0x100000,
section_alignment: 0x1000,
file_alignment: 0x200,
stack_reserve: 0x100000,
stack_commit: 0x1000,
heap_reserve: 0x100000,
heap_commit: 0x1000,
extra_flags: vec!["-m32".into(), "-ffreestanding".into(), "-nostdlib".into()],
}
}
pub fn multiboot2() -> Self {
let mut t = Self::multiboot();
t.triple = "x86_64-unknown-none-elf".into();
t.bitness = X86Bitness::Bits64;
t.cpu_model = "x86-64".into();
t.extra_flags.retain(|f| f != "-m32");
t.extra_flags.push("-m64".into());
t
}
pub fn coreboot() -> Self {
Self {
kind: X86BareMetalTargetKind::Coreboot,
bitness: X86Bitness::Bits64,
triple: "x86_64-unknown-none-elf".into(),
cpu_features: vec!["+mmx".into(), "+sse".into(), "+sse2".into()],
cpu_model: "x86-64".into(),
output_format: "elf".into(),
entry_point: "_start".into(),
pe_subsystem: None,
image_base: 0x100000,
section_alignment: 0x1000,
file_alignment: 0x200,
stack_reserve: 0x100000,
stack_commit: 0x1000,
heap_reserve: 0x100000,
heap_commit: 0x1000,
extra_flags: vec!["-ffreestanding".into(), "-nostdlib".into()],
}
}
pub fn linux_kernel_module() -> Self {
Self {
kind: X86BareMetalTargetKind::LinuxKernelModule,
bitness: X86Bitness::Bits64,
triple: "x86_64-linux-gnu".into(),
cpu_features: vec!["+mmx".into(), "+sse".into(), "+sse2".into()],
cpu_model: "x86-64".into(),
output_format: "elf".into(),
entry_point: "init_module".into(),
pe_subsystem: None,
image_base: 0x0,
section_alignment: 0x1000,
file_alignment: 0x200,
stack_reserve: 0x2000,
stack_commit: 0x1000,
heap_reserve: 0,
heap_commit: 0,
extra_flags: vec![
"-ffreestanding".into(),
"-nostdlib".into(),
"-fno-pic".into(),
"-mcmodel=kernel".into(),
],
}
}
pub fn efi_byte_code() -> Self {
Self {
kind: X86BareMetalTargetKind::EfiByteCode,
bitness: X86Bitness::Bits64,
triple: "x86_64-unknown-efi".into(),
cpu_features: vec![],
cpu_model: "generic".into(),
output_format: "ebc".into(),
entry_point: "EbcMain".into(),
pe_subsystem: Some("EFI_APPLICATION".into()),
image_base: 0x0,
section_alignment: 0x1000,
file_alignment: 0x200,
stack_reserve: 0x10000,
stack_commit: 0x1000,
heap_reserve: 0x10000,
heap_commit: 0x1000,
extra_flags: vec!["-ffreestanding".into(), "-nostdlib".into()],
}
}
pub fn custom(triple: &str) -> Self {
let mut t = Self::new();
t.kind = X86BareMetalTargetKind::Custom;
t.triple = triple.to_string();
t
}
pub fn generate_flags(&self) -> Vec<String> {
let mut flags = Vec::new();
flags.push(format!("-target"));
flags.push(self.triple.clone());
flags.push(format!("-mcpu={}", self.cpu_model));
if !self.cpu_features.is_empty() {
flags.push(format!("-mattr={}", self.cpu_features.join(",")));
}
flags.push(self.bitness.march_flag().to_string());
flags.push(format!("-Wl,--entry={}", self.entry_point));
match self.output_format.as_str() {
"pe" => {
flags.push("-Wl,--subsystem,efi_application".into());
}
_ => {}
}
flags.extend(self.extra_flags.clone());
flags
}
pub fn llvm_triple(&self) -> String {
self.triple.clone()
}
pub fn is_pe_output(&self) -> bool {
self.output_format == "pe"
}
pub fn is_elf_output(&self) -> bool {
self.output_format == "elf"
}
pub fn code_model(&self) -> &'static str {
match self.kind {
X86BareMetalTargetKind::LinuxKernelModule => "kernel",
X86BareMetalTargetKind::BiosBoot => "small",
_ => {
if self.bitness.is_long_mode() {
"large"
} else {
"small"
}
}
}
}
pub fn relocation_model(&self) -> &'static str {
match self.kind {
X86BareMetalTargetKind::UefiBoot | X86BareMetalTargetKind::EfiByteCode => "pic",
X86BareMetalTargetKind::LinuxKernelModule => "static",
_ => "static",
}
}
}
impl Default for X86BareMetalTarget {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BootLoaderKind {
BiosBootSector,
UefiApplication,
MultibootV1,
MultibootV2,
LinuxBzImage,
CorebootPayload,
Custom,
}
impl BootLoaderKind {
pub fn as_str(&self) -> &'static str {
match self {
Self::BiosBootSector => "bios-boot-sector",
Self::UefiApplication => "uefi-application",
Self::MultibootV1 => "multiboot-v1",
Self::MultibootV2 => "multiboot-v2",
Self::LinuxBzImage => "linux-bzimage",
Self::CorebootPayload => "coreboot-payload",
Self::Custom => "custom",
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct BiosDap {
pub size: u8,
pub reserved: u8,
pub sector_count: u16,
pub buffer_offset: u16,
pub buffer_segment: u16,
pub lba_low: u32,
pub lba_high: u32,
}
impl BiosDap {
pub fn new(lba: u64, count: u16, buffer_seg: u16, buffer_off: u16) -> Self {
Self {
size: 16,
reserved: 0,
sector_count: count,
buffer_offset: buffer_off,
buffer_segment: buffer_seg,
lba_low: lba as u32,
lba_high: (lba >> 32) as u32,
}
}
pub fn to_bytes(&self) -> [u8; 16] {
let mut buf = [0u8; 16];
buf[0] = self.size;
buf[1] = self.reserved;
buf[2] = self.sector_count as u8;
buf[3] = (self.sector_count >> 8) as u8;
buf[4] = self.buffer_offset as u8;
buf[5] = (self.buffer_offset >> 8) as u8;
buf[6] = self.buffer_segment as u8;
buf[7] = (self.buffer_segment >> 8) as u8;
buf[8] = self.lba_low as u8;
buf[9] = (self.lba_low >> 8) as u8;
buf[10] = (self.lba_low >> 16) as u8;
buf[11] = (self.lba_low >> 24) as u8;
buf[12] = self.lba_high as u8;
buf[13] = (self.lba_high >> 8) as u8;
buf[14] = (self.lba_high >> 16) as u8;
buf[15] = (self.lba_high >> 24) as u8;
buf
}
}
#[derive(Debug, Clone)]
pub struct MultibootHeader {
pub magic: u32,
pub flags: u32,
pub checksum: u32,
pub header_addr: u32,
pub load_addr: u32,
pub load_end_addr: u32,
pub bss_end_addr: u32,
pub entry_addr: u32,
pub mode_type: u32,
pub width: u32,
pub height: u32,
pub depth: u32,
}
impl MultibootHeader {
pub fn new() -> Self {
let magic: u32 = 0x1BADB002;
let flags: u32 = 0x00000003; let checksum: u32 = 0u32.wrapping_sub(magic).wrapping_sub(flags);
Self {
magic,
flags,
checksum,
header_addr: 0,
load_addr: 0,
load_end_addr: 0,
bss_end_addr: 0,
entry_addr: 0,
mode_type: 0,
width: 0,
height: 0,
depth: 0,
}
}
pub fn new_full(
entry: u32,
load_addr: u32,
load_end: u32,
bss_end: u32,
width: u32,
height: u32,
depth: u32,
) -> Self {
let magic: u32 = 0x1BADB002;
let flags: u32 = 0x00010003; let checksum = 0u32.wrapping_sub(magic).wrapping_sub(flags);
Self {
magic,
flags,
checksum,
header_addr: 0,
load_addr,
load_end_addr: load_end,
bss_end_addr: bss_end,
entry_addr: entry,
mode_type: 1, width,
height,
depth,
}
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(48);
buf.extend_from_slice(&self.magic.to_le_bytes());
buf.extend_from_slice(&self.flags.to_le_bytes());
buf.extend_from_slice(&self.checksum.to_le_bytes());
if self.flags & 0x00010000 != 0 {
buf.extend_from_slice(&self.header_addr.to_le_bytes());
buf.extend_from_slice(&self.load_addr.to_le_bytes());
buf.extend_from_slice(&self.load_end_addr.to_le_bytes());
buf.extend_from_slice(&self.bss_end_addr.to_le_bytes());
buf.extend_from_slice(&self.entry_addr.to_le_bytes());
}
if self.flags & 0x00000004 != 0 {
buf.extend_from_slice(&self.mode_type.to_le_bytes());
buf.extend_from_slice(&self.width.to_le_bytes());
buf.extend_from_slice(&self.height.to_le_bytes());
buf.extend_from_slice(&self.depth.to_le_bytes());
}
buf
}
pub fn to_asm(&self) -> String {
let mut asm = String::new();
asm.push_str("; Multiboot v1 header\n");
asm.push_str("section .multiboot\n");
asm.push_str("align 4\n");
asm.push_str(&format!("dd 0x{:08X} ; magic\n", self.magic));
asm.push_str(&format!("dd 0x{:08X} ; flags\n", self.flags));
asm.push_str(&format!("dd 0x{:08X} ; checksum\n", self.checksum));
if self.flags & 0x00010000 != 0 {
asm.push_str(&format!("dd 0x{:08X} ; header_addr\n", self.header_addr));
asm.push_str(&format!("dd 0x{:08X} ; load_addr\n", self.load_addr));
asm.push_str(&format!(
"dd 0x{:08X} ; load_end_addr\n",
self.load_end_addr
));
asm.push_str(&format!("dd 0x{:08X} ; bss_end_addr\n", self.bss_end_addr));
asm.push_str(&format!("dd 0x{:08X} ; entry_addr\n", self.entry_addr));
}
if self.flags & 0x00000004 != 0 {
asm.push_str(&format!("dd 0x{:08X} ; mode_type\n", self.mode_type));
asm.push_str(&format!("dd 0x{:08X} ; width\n", self.width));
asm.push_str(&format!("dd 0x{:08X} ; height\n", self.height));
asm.push_str(&format!("dd 0x{:08X} ; depth\n", self.depth));
}
asm
}
}
impl Default for MultibootHeader {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Multiboot2TagType {
End = 0,
InformationRequest = 1,
Address = 2,
EntryAddress = 3,
ConsoleFlags = 4,
Framebuffer = 5,
ModuleAlign = 6,
EfiBootServices = 7,
EfiI386Entry = 8,
EfiAmd64Entry = 9,
RelocatableHeader = 10,
}
#[derive(Debug, Clone)]
pub struct Multiboot2Tag {
pub tag_type: Multiboot2TagType,
pub flags: u32,
pub data: Vec<u8>,
}
impl Multiboot2Tag {
pub fn new(tag_type: Multiboot2TagType) -> Self {
Self {
tag_type,
flags: 0,
data: Vec::new(),
}
}
pub fn to_bytes(&self) -> Vec<u8> {
let data_len = self.data.len() as u32;
let size = 8 + data_len; let padded = (size + 7) & !7; let mut buf = Vec::with_capacity(padded as usize);
buf.extend_from_slice(&(self.tag_type as u16).to_le_bytes());
buf.extend_from_slice(&(self.flags as u16).to_le_bytes());
buf.extend_from_slice(&padded.to_le_bytes());
buf.extend_from_slice(&self.data);
while buf.len() < padded as usize {
buf.push(0);
}
buf
}
}
#[derive(Debug, Clone)]
pub struct Multiboot2Header {
pub architecture: u32, pub header_addr: u32,
pub load_addr: u32,
pub load_end_addr: u32,
pub bss_end_addr: u32,
pub entry_addr: u32,
pub tags: Vec<Multiboot2Tag>,
}
impl Multiboot2Header {
pub fn new_i386() -> Self {
Self {
architecture: 0,
header_addr: 0,
load_addr: 0,
load_end_addr: 0,
bss_end_addr: 0,
entry_addr: 0,
tags: Vec::new(),
}
}
pub fn add_tag(mut self, tag: Multiboot2Tag) -> Self {
self.tags.push(tag);
self
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut buf = Vec::new();
buf.extend_from_slice(&0xE85250D6u32.to_le_bytes());
buf.extend_from_slice(&self.architecture.to_le_bytes());
let header_len: u32 = 24
+ self
.tags
.iter()
.map(|t| {
let sz = 8 + t.data.len() as u32;
(sz + 7) & !7
})
.sum::<u32>();
buf.extend_from_slice(&header_len.to_le_bytes());
let checksum_pos = buf.len();
buf.extend_from_slice(&0u32.to_le_bytes());
for tag in &self.tags {
buf.extend_from_slice(&tag.to_bytes());
}
buf.extend_from_slice(&0u16.to_le_bytes());
buf.extend_from_slice(&0u16.to_le_bytes());
buf.extend_from_slice(&8u32.to_le_bytes());
let checksum = 0u32
.wrapping_sub(0xE85250D6)
.wrapping_sub(header_len)
.wrapping_sub(
buf.iter()
.skip(checksum_pos + 4)
.fold(0u32, |acc, &b| acc.wrapping_add(b as u32)),
);
buf[checksum_pos..checksum_pos + 4].copy_from_slice(&checksum.to_le_bytes());
buf
}
}
#[derive(Debug, Clone)]
pub struct X86BootLoader {
pub kind: BootLoaderKind,
pub bitness: X86Bitness,
pub load_address: u64,
pub entry_address: u64,
pub multiboot_header: Option<MultibootHeader>,
pub multiboot2_header: Option<Multiboot2Header>,
pub kernel_image_path: Option<String>,
pub boot_message: Option<String>,
pub video_mode: Option<(u32, u32, u32)>, }
impl X86BootLoader {
pub fn new(kind: BootLoaderKind) -> Self {
let bitness = match kind {
BootLoaderKind::BiosBootSector => X86Bitness::Bits16,
BootLoaderKind::MultibootV1 | BootLoaderKind::MultibootV2 => X86Bitness::Bits32,
_ => X86Bitness::Bits64,
};
Self {
kind,
bitness,
load_address: match kind {
BootLoaderKind::BiosBootSector => 0x7C00,
BootLoaderKind::MultibootV1 | BootLoaderKind::MultibootV2 => 0x100000,
_ => 0x1000000,
},
entry_address: 0,
multiboot_header: if kind == BootLoaderKind::MultibootV1 {
Some(MultibootHeader::new())
} else {
None
},
multiboot2_header: if kind == BootLoaderKind::MultibootV2 {
Some(Multiboot2Header::new_i386())
} else {
None
},
kernel_image_path: None,
boot_message: None,
video_mode: None,
}
}
pub fn bios_boot_sector() -> Self {
Self::new(BootLoaderKind::BiosBootSector)
}
pub fn uefi_application() -> Self {
Self::new(BootLoaderKind::UefiApplication)
}
pub fn multiboot_v1() -> Self {
Self::new(BootLoaderKind::MultibootV1)
}
pub fn multiboot_v2() -> Self {
Self::new(BootLoaderKind::MultibootV2)
}
pub fn linux_bzimage() -> Self {
Self::new(BootLoaderKind::LinuxBzImage)
}
pub fn coreboot_payload() -> Self {
Self::new(BootLoaderKind::CorebootPayload)
}
pub fn generate_boot_sector(&self) -> Option<Vec<u8>> {
if self.kind != BootLoaderKind::BiosBootSector {
return None;
}
let mut boot = vec![0u8; 512];
boot[0] = 0xEB; boot[1] = 0x3C; boot[2] = 0x90;
let bpb = b"MSDOS5.0";
boot[3..11].copy_from_slice(bpb);
boot[11] = 0x00;
boot[12] = 0x02;
boot[13] = 0x01;
boot[14] = 0x01;
boot[15] = 0x00;
boot[16] = 0x02;
boot[17] = 0xE0;
boot[18] = 0x00;
boot[19] = 0x40;
boot[20] = 0x0B;
boot[21] = 0xF8;
boot[22] = 0x09;
boot[23] = 0x00;
boot[24] = 0x12;
boot[25] = 0x00;
boot[26] = 0x02;
boot[27] = 0x00;
boot[28] = 0x00;
boot[29] = 0x00;
boot[30] = 0x00;
boot[31] = 0x00;
boot[32] = 0x00;
boot[33] = 0x00;
boot[34] = 0x00;
boot[35] = 0x00;
boot[36] = 0x00;
boot[37] = 0x00;
boot[38] = 0x29;
boot[39] = 0x12;
boot[40] = 0x34;
boot[41] = 0x56;
boot[42] = 0x78;
let label = b"LLVM BOOT ";
boot[43..54].copy_from_slice(label);
let fstype = b"FAT12 ";
boot[54..62].copy_from_slice(fstype);
let code: [u8; 30] = [
0xFA, 0x31, 0xC0, 0x8E, 0xD8, 0x8E, 0xC0, 0x8E, 0xD0, 0xBC, 0x00, 0x7C, 0xFB, 0xB4, 0x0E, 0xBE, 0x10, 0x7C, 0xAC, 0x08, 0xC0, 0x74, 0x04, 0xCD, 0x10, 0xEB, 0xF7, 0xF4, 0xEB,
0xFD, ];
boot[62..62 + code.len()].copy_from_slice(&code[..]);
let msg = self
.boot_message
.clone()
.unwrap_or_else(|| "LLVM-Native BIOS Boot".into());
let msg_start = 62 + code.len();
let msg_bytes = msg.as_bytes();
let max_msg = (510 - msg_start).min(msg_bytes.len());
boot[msg_start..msg_start + max_msg].copy_from_slice(&msg_bytes[..max_msg]);
boot[msg_start + max_msg] = 0;
boot[510] = 0x55;
boot[511] = 0xAA;
Some(boot)
}
pub fn generate_boot_sector_asm(&self) -> String {
let mut asm = String::new();
asm.push_str("; X86 BIOS Boot Sector — generated by llvm-native\n");
asm.push_str("; 512 bytes, loaded at 0x7C00, boots into protected/long mode\n\n");
asm.push_str("bits 16\n");
asm.push_str("org 0x7C00\n\n");
asm.push_str("start:\n");
asm.push_str(" cli\n");
asm.push_str(" xor ax, ax\n");
asm.push_str(" mov ds, ax\n");
asm.push_str(" mov es, ax\n");
asm.push_str(" mov ss, ax\n");
asm.push_str(" mov sp, 0x7C00\n");
asm.push_str(" sti\n\n");
asm.push_str(" ; Load kernel from disk\n");
asm.push_str(" mov ah, 0x42 ; extended read\n");
asm.push_str(" mov dl, [boot_drive]\n");
asm.push_str(" mov si, dap\n");
asm.push_str(" int 0x13\n");
asm.push_str(" jc disk_error\n\n");
asm.push_str(" ; Check if we should enable A20\n");
asm.push_str(" call enable_a20\n\n");
asm.push_str(" ; Switch to protected mode\n");
asm.push_str(" cli\n");
asm.push_str(" lgdt [gdt_descriptor]\n");
asm.push_str(" mov eax, cr0\n");
asm.push_str(" or eax, 1\n");
asm.push_str(" mov cr0, eax\n");
asm.push_str(" jmp 0x08:protected_mode_entry\n\n");
asm.push_str("bits 32\n");
asm.push_str("protected_mode_entry:\n");
asm.push_str(" mov ax, 0x10\n");
asm.push_str(" mov ds, ax\n");
asm.push_str(" mov es, ax\n");
asm.push_str(" mov fs, ax\n");
asm.push_str(" mov gs, ax\n");
asm.push_str(" mov ss, ax\n");
asm.push_str(" mov esp, 0x90000\n");
asm.push_str(" call 0x100000 ; jump to kernel\n\n");
asm.push_str("enable_a20:\n");
asm.push_str(" in al, 0x92\n");
asm.push_str(" or al, 2\n");
asm.push_str(" out 0x92, al\n");
asm.push_str(" ret\n\n");
asm.push_str("disk_error:\n");
asm.push_str(" mov si, error_msg\n");
asm.push_str(" call print_string\n");
asm.push_str(" hlt\n");
asm.push_str(" jmp $\n\n");
asm.push_str("print_string:\n");
asm.push_str(" lodsb\n");
asm.push_str(" or al, al\n");
asm.push_str(" jz .done\n");
asm.push_str(" mov ah, 0x0E\n");
asm.push_str(" int 0x10\n");
asm.push_str(" jmp print_string\n");
asm.push_str(".done: ret\n\n");
asm.push_str("; GDT\n");
asm.push_str("gdt_start:\n");
asm.push_str(" dq 0 ; null descriptor\n");
asm.push_str(" dq 0x00CF9A000000FFFF ; 32-bit code\n");
asm.push_str(" dq 0x00CF92000000FFFF ; 32-bit data\n");
asm.push_str("gdt_descriptor:\n");
asm.push_str(" dw $ - gdt_start - 1\n");
asm.push_str(" dd gdt_start\n\n");
asm.push_str("; DAP for INT 13h extended read\n");
asm.push_str("dap:\n");
asm.push_str(" db 0x10 ; size of DAP\n");
asm.push_str(" db 0 ; reserved\n");
asm.push_str(" dw 64 ; sector count\n");
asm.push_str(" dw 0x0000 ; buffer offset\n");
asm.push_str(" dw 0x1000 ; buffer segment\n");
asm.push_str(" dq 1 ; starting LBA\n\n");
asm.push_str("boot_drive: db 0x80\n");
asm.push_str("error_msg: db 'Disk error!', 0\n\n");
asm.push_str("times 510 - ($ - $$) db 0\n");
asm.push_str("dw 0xAA55\n");
asm
}
pub fn generate_uefi_entry_c(&self) -> String {
let mut c = String::new();
c.push_str("/* UEFI Application Entry Point — generated by llvm-native */\n\n");
c.push_str("#include <efi.h>\n");
c.push_str("#include <efilib.h>\n\n");
c.push_str("EFI_STATUS\n");
c.push_str("EFIAPI\n");
c.push_str("efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) {\n");
c.push_str(" InitializeLib(ImageHandle, SystemTable);\n");
c.push_str(" Print(L\"LLVM-Native UEFI Boot\\n\");\n");
c.push_str(" \n");
c.push_str(" // Call the kernel entry point\n");
c.push_str(" kernel_main();\n");
c.push_str(" \n");
c.push_str(" // Should not return\n");
c.push_str(" return EFI_SUCCESS;\n");
c.push_str("}\n");
c
}
pub fn generate_bzimage_setup(&self) -> Vec<u8> {
let mut setup = vec![0u8; 512];
let header_off = 0x1F1;
setup[header_off] = 0x04; setup[header_off + 1] = 0x00;
setup[header_off + 2] = 0x00;
let syssize: u32 = 0x1000;
setup[header_off + 4] = syssize as u8;
setup[header_off + 5] = (syssize >> 8) as u8;
setup[header_off + 6] = (syssize >> 16) as u8;
setup[header_off + 7] = (syssize >> 24) as u8;
setup[header_off + 8] = 0x00;
setup[header_off + 9] = 0x00;
setup[header_off + 10] = 0xFF;
setup[header_off + 11] = 0xFF;
setup[header_off + 12] = 0x00;
setup[header_off + 13] = 0x00;
setup[header_off + 14] = 0x55;
setup[header_off + 15] = 0xAA;
setup[header_off + 16] = 0xEB; setup[header_off + 18] = b'H';
setup[header_off + 19] = b'd';
setup[header_off + 20] = b'r';
setup[header_off + 21] = b'S';
setup[header_off + 22] = 0x0E;
setup[header_off + 23] = 0x02;
setup[header_off + 24] = 0x00;
setup[header_off + 25] = 0x00;
setup[header_off + 26] = 0x00;
setup[header_off + 27] = 0x00;
setup[header_off + 28] = 0x00;
setup[header_off + 29] = 0x10;
setup[header_off + 30] = 0x00;
setup[header_off + 31] = 0x00;
setup[header_off + 32] = 0xFF;
setup[header_off + 33] = 0x81; setup[header_off + 34] = 0x00;
setup[header_off + 35] = 0x00;
setup[header_off + 36] = 0x00;
setup[header_off + 37] = 0x00;
setup[header_off + 38] = 0x10;
setup[header_off + 39] = 0x00;
setup[header_off + 40] = 0x00; setup[header_off + 44] = 0x00;
setup[header_off + 48] = 0x00;
setup[header_off + 52] = 0x00;
setup[header_off + 53] = 0x90;
setup[header_off + 54] = 0x00;
setup[header_off + 55] = 0x00;
setup[header_off + 56] = 0x00; setup[header_off + 60] = 0xFF;
setup[header_off + 61] = 0xFF;
setup[header_off + 62] = 0xFF;
setup[header_off + 63] = 0xFF;
setup[header_off + 64] = 0x00;
setup[header_off + 65] = 0x00;
setup[header_off + 66] = 0x10;
setup[header_off + 67] = 0x00;
setup[header_off + 68] = 0x01;
setup[header_off + 69] = 0x00;
setup[header_off + 70] = 0x10;
setup[header_off + 71] = 0x03;
setup[header_off + 72] = 0x00;
setup[header_off + 73] = 0xFF;
setup[header_off + 74] = 0x07;
setup[header_off + 75] = 0x00; setup[header_off + 76] = 0x00; setup[header_off + 88] = 0x00;
setup[header_off + 89] = 0x02;
setup[header_off + 90] = 0x00;
setup[header_off + 91] = 0x00;
setup
}
pub fn generate_coreboot_header(&self) -> String {
let mut c = String::new();
c.push_str("/* Coreboot Payload Header — generated by llvm-native */\n\n");
c.push_str("#include <stdint.h>\n\n");
c.push_str("struct cb_header {\n");
c.push_str(" uint32_t signature; /* LBIO */\n");
c.push_str(" uint32_t header_bytes;\n");
c.push_str(" uint32_t header_checksum;\n");
c.push_str(" uint32_t table_bytes;\n");
c.push_str(" uint32_t table_checksum;\n");
c.push_str(" uint32_t table_entries;\n");
c.push_str("};\n\n");
c.push_str("void coreboot_entry(void *cb_header_ptr) {\n");
c.push_str(" /* Parse coreboot tables */\n");
c.push_str(" struct cb_header *hdr = (struct cb_header *)cb_header_ptr;\n");
c.push_str(" /* Call kernel main */\n");
c.push_str(" kernel_main();\n");
c.push_str("}\n");
c
}
pub fn with_boot_message(mut self, msg: &str) -> Self {
self.boot_message = Some(msg.to_string());
self
}
pub fn with_video_mode(mut self, width: u32, height: u32, depth: u32) -> Self {
self.video_mode = Some((width, height, depth));
self
}
pub fn with_load_address(mut self, addr: u64) -> Self {
self.load_address = addr;
self
}
pub fn with_entry_address(mut self, addr: u64) -> Self {
self.entry_address = addr;
self
}
}
impl Default for X86BootLoader {
fn default() -> Self {
Self::new(BootLoaderKind::BiosBootSector)
}
}
#[derive(Debug, Clone)]
pub struct X86MemoryRegion {
pub name: String,
pub origin: u64,
pub length: u64,
pub attributes: String,
}
impl X86MemoryRegion {
pub fn new(name: &str, origin: u64, length: u64, attrs: &str) -> Self {
Self {
name: name.to_string(),
origin,
length,
attributes: attrs.to_string(),
}
}
pub fn rom_4m() -> Self {
Self::new("ROM", 0xFFFF_FFF0, 0x10, "rx")
}
pub fn ram_1m() -> Self {
Self::new("RAM", 0x0010_0000, 0x0FF0_0000, "rwx")
}
pub fn ram_64() -> Self {
Self::new("RAM64", 0x0010_0000, 0x3FE0_0000, "rwx")
}
pub fn low_ram() -> Self {
Self::new("LOW_RAM", 0x0000_0500, 0x0007_BB00, "rwx")
}
pub fn uefi_ram() -> Self {
Self::new("UEFI_RAM", 0x0000_0000, 0x8000_0000, "rwx")
}
}
#[derive(Debug, Clone)]
pub struct X86SectionPlacement {
pub name: String,
pub region: String,
pub load_address: Option<u64>,
pub alignment: u64,
pub keep: bool,
pub input_pattern: String,
}
impl X86SectionPlacement {
pub fn new(name: &str, region: &str) -> Self {
Self {
name: name.to_string(),
region: region.to_string(),
load_address: None,
alignment: 0x10,
keep: false,
input_pattern: format!("*({}*)", name),
}
}
pub fn with_alignment(mut self, align: u64) -> Self {
self.alignment = align;
self
}
pub fn with_keep(mut self, k: bool) -> Self {
self.keep = k;
self
}
pub fn with_load_address(mut self, addr: u64) -> Self {
self.load_address = Some(addr);
self
}
pub fn with_input_pattern(mut self, pat: &str) -> Self {
self.input_pattern = pat.to_string();
self
}
}
#[derive(Debug, Clone)]
pub struct X86LinkerScript {
pub filename: Option<String>,
pub arch: String,
pub bitness: X86Bitness,
pub memory_regions: Vec<X86MemoryRegion>,
pub sections: Vec<X86SectionPlacement>,
pub entry_point: String,
pub output_format: String,
pub start_address: Option<u64>,
pub stack_size: u64,
pub heap_size: u64,
pub page_size: u64,
pub generate_build_id: bool,
pub extra_commands: Vec<String>,
}
impl X86LinkerScript {
pub fn new() -> Self {
Self {
filename: Some("linker.ld".into()),
arch: "i386:x86-64".into(),
bitness: X86Bitness::Bits64,
memory_regions: vec![X86MemoryRegion::ram_64()],
sections: vec![
X86SectionPlacement::new(".text", "RAM64").with_alignment(0x1000),
X86SectionPlacement::new(".rodata", "RAM64").with_alignment(0x1000),
X86SectionPlacement::new(".data", "RAM64").with_alignment(0x1000),
X86SectionPlacement::new(".bss", "RAM64").with_alignment(0x1000),
],
entry_point: "_start".into(),
output_format: "elf64-x86-64".into(),
start_address: Some(0x100000),
stack_size: 65536,
heap_size: 1048576,
page_size: 4096,
generate_build_id: false,
extra_commands: Vec::new(),
}
}
pub fn new_32bit() -> Self {
Self {
filename: Some("linker.ld".into()),
arch: "i386".into(),
bitness: X86Bitness::Bits32,
memory_regions: vec![X86MemoryRegion::ram_1m()],
sections: vec![
X86SectionPlacement::new(".text", "RAM").with_alignment(0x1000),
X86SectionPlacement::new(".rodata", "RAM").with_alignment(0x1000),
X86SectionPlacement::new(".data", "RAM").with_alignment(0x1000),
X86SectionPlacement::new(".bss", "RAM").with_alignment(0x1000),
],
entry_point: "_start".into(),
output_format: "elf32-i386".into(),
start_address: Some(0x100000),
stack_size: 32768,
heap_size: 524288,
page_size: 4096,
generate_build_id: false,
extra_commands: Vec::new(),
}
}
pub fn new_bios() -> Self {
Self {
filename: Some("linker.ld".into()),
arch: "i386".into(),
bitness: X86Bitness::Bits16,
memory_regions: vec![
X86MemoryRegion::new("BOOT", 0x7C00, 0x200, "rx"),
X86MemoryRegion::new("LOW_RAM", 0x0500, 0x7700, "rwx"),
],
sections: vec![
X86SectionPlacement::new(".text", "BOOT").with_alignment(0x200),
X86SectionPlacement::new(".data", "LOW_RAM").with_alignment(0x10),
],
entry_point: "_start16".into(),
output_format: "binary".into(),
start_address: Some(0x7C00),
stack_size: 2048,
heap_size: 0,
page_size: 512,
generate_build_id: false,
extra_commands: Vec::new(),
}
}
pub fn new_uefi() -> Self {
Self {
filename: Some("uefi.ld".into()),
arch: "i386:x86-64".into(),
bitness: X86Bitness::Bits64,
memory_regions: vec![X86MemoryRegion::uefi_ram()],
sections: vec![
X86SectionPlacement::new(".text", "UEFI_RAM").with_alignment(0x1000),
X86SectionPlacement::new(".rodata", "UEFI_RAM").with_alignment(0x1000),
X86SectionPlacement::new(".data", "UEFI_RAM").with_alignment(0x1000),
X86SectionPlacement::new(".reloc", "UEFI_RAM").with_alignment(0x1000),
],
entry_point: "efi_main".into(),
output_format: "pei-x86-64".into(),
start_address: None,
stack_size: 65536,
heap_size: 65536,
page_size: 4096,
generate_build_id: false,
extra_commands: Vec::new(),
}
}
pub fn new_multiboot() -> Self {
let mut s = Self::new_32bit();
s.filename = Some("multiboot.ld".into());
s.sections.insert(
0,
X86SectionPlacement::new(".multiboot", "RAM")
.with_alignment(0x04)
.with_input_pattern("*(.multiboot*)"),
);
s
}
pub fn new_multiboot2() -> Self {
let mut s = Self::new();
s.filename = Some("multiboot2.ld".into());
s.sections.insert(
0,
X86SectionPlacement::new(".multiboot2", "RAM64")
.with_alignment(0x08)
.with_input_pattern("*(.multiboot2*)"),
);
s
}
pub fn generate(&self) -> String {
let mut ld = String::new();
ld.push_str(&format!(
"/* Linker script for {} — generated by llvm-native */\n\n",
self.arch
));
if !self.output_format.is_empty() {
ld.push_str(&format!("OUTPUT_FORMAT(\"{}\")\n", self.output_format));
}
ld.push_str(&format!("OUTPUT_ARCH({})\n", self.arch));
ld.push_str(&format!("ENTRY({})\n\n", self.entry_point));
if let Some(addr) = self.start_address {
ld.push_str(&format!("STARTUP(0x{:X})\n\n", addr));
}
if !self.memory_regions.is_empty() {
ld.push_str("MEMORY\n{\n");
for region in &self.memory_regions {
ld.push_str(&format!(
" {} ({}): ORIGIN = 0x{:X}, LENGTH = 0x{:X}\n",
region.name, region.attributes, region.origin, region.length
));
}
ld.push_str("}\n\n");
}
if self.bitness != X86Bitness::Bits16 {
ld.push_str(&format!("__PAGE_SIZE = {};\n\n", self.page_size));
}
ld.push_str("SECTIONS\n{\n");
ld.push_str(" . = 0x100000;\n\n");
for section in &self.sections {
if let Some(lma) = section.load_address {
ld.push_str(&format!(
" {} 0x{:X} : AT(0x{:X}) {{\n",
section.name, lma, lma
));
} else if !section.region.is_empty() {
ld.push_str(&format!(" {} : {{\n", section.name));
} else {
ld.push_str(&format!(" {} : {{\n", section.name));
}
if section.keep {
ld.push_str(" KEEP(");
}
ld.push_str(&format!(" {}\n", section.input_pattern));
if section.keep {
ld.push_str(")");
}
if section.alignment > 1 {
ld.push_str(&format!(" . = ALIGN(0x{:X});\n", section.alignment));
}
if !section.region.is_empty() {
ld.push_str(&format!(" }} > {}\n\n", section.region));
} else {
ld.push_str(" }\n\n");
}
}
ld.push_str(" /* Stack */\n");
ld.push_str(&format!(" . = ALIGN(0x{:X});\n", self.page_size));
ld.push_str(" __stack_start = .;\n");
ld.push_str(&format!(" . = . + 0x{:X};\n", self.stack_size));
ld.push_str(" __stack_top = .;\n");
ld.push_str(" stack_top = __stack_top;\n\n");
ld.push_str(" /* Heap */\n");
ld.push_str(&format!(" . = ALIGN(0x{:X});\n", self.page_size));
ld.push_str(" __heap_start = .;\n");
ld.push_str(&format!(" . = . + 0x{:X};\n", self.heap_size));
ld.push_str(" __heap_end = .;\n\n");
ld.push_str(" /* BSS boundaries for CRT0 */\n");
ld.push_str(" __bss_start = ADDR(.bss);\n");
ld.push_str(" __bss_end = ADDR(.bss) + SIZEOF(.bss);\n");
ld.push_str(" /* Data boundaries */\n");
ld.push_str(" __data_start = ADDR(.data);\n");
ld.push_str(" __data_end = ADDR(.data) + SIZEOF(.data);\n");
ld.push_str(" __data_load_start = LOADADDR(.data);\n\n");
for cmd in &self.extra_commands {
ld.push_str(&format!(" {}\n", cmd));
}
ld.push_str(" /* Discard sections */\n");
ld.push_str(" /DISCARD/ : {\n");
ld.push_str(" *(.comment)\n");
ld.push_str(" *(.note.GNU-stack)\n");
ld.push_str(" *(.eh_frame)\n");
ld.push_str(" }\n");
ld.push_str("}\n");
ld
}
pub fn add_memory_region(mut self, region: X86MemoryRegion) -> Self {
self.memory_regions.push(region);
self
}
pub fn add_section(mut self, section: X86SectionPlacement) -> Self {
self.sections.push(section);
self
}
pub fn with_stack_size(mut self, bytes: u64) -> Self {
self.stack_size = bytes;
self
}
pub fn with_heap_size(mut self, bytes: u64) -> Self {
self.heap_size = bytes;
self
}
pub fn with_entry(mut self, entry: &str) -> Self {
self.entry_point = entry.to_string();
self
}
pub fn with_output_format(mut self, fmt: &str) -> Self {
self.output_format = fmt.to_string();
self
}
pub fn with_command(mut self, cmd: &str) -> Self {
self.extra_commands.push(cmd.to_string());
self
}
pub fn get_region(&self, name: &str) -> Option<&X86MemoryRegion> {
self.memory_regions.iter().find(|r| r.name == name)
}
pub fn region_count(&self) -> usize {
self.memory_regions.len()
}
pub fn section_count(&self) -> usize {
self.sections.len()
}
}
impl Default for X86LinkerScript {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EmbeddedX86Intrinsic {
Cli,
Sti,
Hlt,
Pause,
Lidt,
Sidt,
Lgdt,
Sgdt,
Lldt,
Sldt,
Ltr,
Str,
Inb,
Outb,
Inw,
Outw,
Inl,
Outl,
Rdmsr,
Wrmsr,
Rdtsc,
Rdtscp,
Cpuid,
Invlpg,
Wbinvd,
Invd,
Mfence,
Lfence,
Sfence,
Xsetbv,
Xgetbv,
Rdfsbase,
Rdgsbase,
Wrfsbase,
Wrgsbase,
Swapgs,
Serialize,
Rdpmc,
Rdpid,
Clflush,
Clflushopt,
Clwb,
Prefetch,
Prefetchw,
Monitor,
Monitorx,
Mwait,
Mwaitx,
}
impl EmbeddedX86Intrinsic {
pub const COUNT: usize = 47;
pub fn index(&self) -> usize {
match self {
Self::Cli => 0,
Self::Sti => 1,
Self::Hlt => 2,
Self::Pause => 3,
Self::Lidt => 4,
Self::Sidt => 5,
Self::Lgdt => 6,
Self::Sgdt => 7,
Self::Lldt => 8,
Self::Sldt => 9,
Self::Ltr => 10,
Self::Str => 11,
Self::Inb => 12,
Self::Outb => 13,
Self::Inw => 14,
Self::Outw => 15,
Self::Inl => 16,
Self::Outl => 17,
Self::Rdmsr => 18,
Self::Wrmsr => 19,
Self::Rdtsc => 20,
Self::Rdtscp => 21,
Self::Cpuid => 22,
Self::Invlpg => 23,
Self::Wbinvd => 24,
Self::Invd => 25,
Self::Mfence => 26,
Self::Lfence => 27,
Self::Sfence => 28,
Self::Xsetbv => 29,
Self::Xgetbv => 30,
Self::Rdfsbase => 31,
Self::Rdgsbase => 32,
Self::Wrfsbase => 33,
Self::Wrgsbase => 34,
Self::Swapgs => 35,
Self::Serialize => 36,
Self::Rdpmc => 37,
Self::Rdpid => 38,
Self::Clflush => 39,
Self::Clflushopt => 40,
Self::Clwb => 41,
Self::Prefetch => 42,
Self::Prefetchw => 43,
Self::Monitor => 44,
Self::Monitorx => 45,
Self::Mwait => 46,
Self::Mwaitx => 47,
}
}
pub fn builtin_name(&self) -> &'static str {
match self {
Self::Cli => "__builtin_ia32_cli",
Self::Sti => "__builtin_ia32_sti",
Self::Hlt => "__builtin_ia32_hlt",
Self::Pause => "__builtin_ia32_pause",
Self::Lidt => "__builtin_ia32_lidt",
Self::Sidt => "__builtin_ia32_sidt",
Self::Lgdt => "__builtin_ia32_lgdt",
Self::Sgdt => "__builtin_ia32_sgdt",
Self::Lldt => "__builtin_ia32_lldt",
Self::Sldt => "__builtin_ia32_sldt",
Self::Ltr => "__builtin_ia32_ltr",
Self::Str => "__builtin_ia32_str",
Self::Inb => "__builtin_ia32_inb",
Self::Outb => "__builtin_ia32_outb",
Self::Inw => "__builtin_ia32_inw",
Self::Outw => "__builtin_ia32_outw",
Self::Inl => "__builtin_ia32_inl",
Self::Outl => "__builtin_ia32_outl",
Self::Rdmsr => "__builtin_ia32_rdmsr",
Self::Wrmsr => "__builtin_ia32_wrmsr",
Self::Rdtsc => "__builtin_ia32_rdtsc",
Self::Rdtscp => "__builtin_ia32_rdtscp",
Self::Cpuid => "__builtin_ia32_cpuid",
Self::Invlpg => "__builtin_ia32_invlpg",
Self::Wbinvd => "__builtin_ia32_wbinvd",
Self::Invd => "__builtin_ia32_invd",
Self::Mfence => "__builtin_ia32_mfence",
Self::Lfence => "__builtin_ia32_lfence",
Self::Sfence => "__builtin_ia32_sfence",
Self::Xsetbv => "__builtin_ia32_xsetbv",
Self::Xgetbv => "__builtin_ia32_xgetbv",
Self::Rdfsbase => "__builtin_ia32_rdfsbase",
Self::Rdgsbase => "__builtin_ia32_rdgsbase",
Self::Wrfsbase => "__builtin_ia32_wrfsbase",
Self::Wrgsbase => "__builtin_ia32_wrgsbase",
Self::Swapgs => "__builtin_ia32_swapgs",
Self::Serialize => "__builtin_ia32_serialize",
Self::Rdpmc => "__builtin_ia32_rdpmc",
Self::Rdpid => "__builtin_ia32_rdpid",
Self::Clflush => "__builtin_ia32_clflush",
Self::Clflushopt => "__builtin_ia32_clflushopt",
Self::Clwb => "__builtin_ia32_clwb",
Self::Prefetch => "__builtin_ia32_prefetch",
Self::Prefetchw => "__builtin_ia32_prefetchw",
Self::Monitor => "__builtin_ia32_monitor",
Self::Monitorx => "__builtin_ia32_monitorx",
Self::Mwait => "__builtin_ia32_mwait",
Self::Mwaitx => "__builtin_ia32_mwaitx",
}
}
pub fn mnemonic(&self) -> &'static str {
match self {
Self::Cli => "cli",
Self::Sti => "sti",
Self::Hlt => "hlt",
Self::Pause => "pause",
Self::Lidt => "lidt",
Self::Sidt => "sidt",
Self::Lgdt => "lgdt",
Self::Sgdt => "sgdt",
Self::Lldt => "lldt",
Self::Sldt => "sldt",
Self::Ltr => "ltr",
Self::Str => "str",
Self::Inb => "inb",
Self::Outb => "outb",
Self::Inw => "inw",
Self::Outw => "outw",
Self::Inl => "inl",
Self::Outl => "outl",
Self::Rdmsr => "rdmsr",
Self::Wrmsr => "wrmsr",
Self::Rdtsc => "rdtsc",
Self::Rdtscp => "rdtscp",
Self::Cpuid => "cpuid",
Self::Invlpg => "invlpg",
Self::Wbinvd => "wbinvd",
Self::Invd => "invd",
Self::Mfence => "mfence",
Self::Lfence => "lfence",
Self::Sfence => "sfence",
Self::Xsetbv => "xsetbv",
Self::Xgetbv => "xgetbv",
Self::Rdfsbase => "rdfsbase",
Self::Rdgsbase => "rdgsbase",
Self::Wrfsbase => "wrfsbase",
Self::Wrgsbase => "wrgsbase",
Self::Swapgs => "swapgs",
Self::Serialize => "serialize",
Self::Rdpmc => "rdpmc",
Self::Rdpid => "rdpid",
Self::Clflush => "clflush",
Self::Clflushopt => "clflushopt",
Self::Clwb => "clwb",
Self::Prefetch => "prefetch",
Self::Prefetchw => "prefetchw",
Self::Monitor => "monitor",
Self::Monitorx => "monitorx",
Self::Mwait => "mwait",
Self::Mwaitx => "mwaitx",
}
}
pub fn category(&self) -> EmbeddedIntrinsicCategory {
match self {
Self::Cli | Self::Sti | Self::Hlt | Self::Pause => EmbeddedIntrinsicCategory::Interrupt,
Self::Lidt
| Self::Sidt
| Self::Lgdt
| Self::Sgdt
| Self::Lldt
| Self::Sldt
| Self::Ltr
| Self::Str => EmbeddedIntrinsicCategory::DescriptorTable,
Self::Inb | Self::Outb | Self::Inw | Self::Outw | Self::Inl | Self::Outl => {
EmbeddedIntrinsicCategory::IoPort
}
Self::Rdmsr | Self::Wrmsr => EmbeddedIntrinsicCategory::Msr,
Self::Rdtsc | Self::Rdtscp => EmbeddedIntrinsicCategory::Timestamp,
Self::Cpuid => EmbeddedIntrinsicCategory::CpuId,
Self::Invlpg | Self::Wbinvd | Self::Invd => EmbeddedIntrinsicCategory::Cache,
Self::Mfence | Self::Lfence | Self::Sfence => EmbeddedIntrinsicCategory::Fence,
Self::Xsetbv | Self::Xgetbv => EmbeddedIntrinsicCategory::ControlRegister,
Self::Rdfsbase | Self::Rdgsbase | Self::Wrfsbase | Self::Wrgsbase => {
EmbeddedIntrinsicCategory::SegmentBase
}
Self::Swapgs | Self::Serialize | Self::Rdpmc | Self::Rdpid => {
EmbeddedIntrinsicCategory::Misc
}
Self::Clflush | Self::Clflushopt | Self::Clwb => EmbeddedIntrinsicCategory::CacheFlush,
Self::Prefetch | Self::Prefetchw => EmbeddedIntrinsicCategory::Cache,
Self::Monitor | Self::Monitorx | Self::Mwait | Self::Mwaitx => {
EmbeddedIntrinsicCategory::PowerManagement
}
}
}
pub fn requires_ring0(&self) -> bool {
matches!(
self,
Self::Cli
| Self::Sti
| Self::Lidt
| Self::Lgdt
| Self::Lldt
| Self::Ltr
| Self::Rdmsr
| Self::Wrmsr
| Self::Invlpg
| Self::Wbinvd
| Self::Invd
| Self::Xsetbv
| Self::Xgetbv
| Self::Swapgs
)
}
pub fn requires_64bit(&self) -> bool {
matches!(
self,
Self::Swapgs | Self::Rdfsbase | Self::Rdgsbase | Self::Wrfsbase | Self::Wrgsbase
)
}
pub fn operand_count(&self) -> usize {
match self {
Self::Inb | Self::Outb | Self::Inw | Self::Outw | Self::Inl | Self::Outl => 2, Self::Rdmsr | Self::Wrmsr => 2, Self::Cpuid => 4, Self::Xsetbv => 2, Self::Xgetbv => 1, Self::Lidt | Self::Sidt | Self::Lgdt | Self::Sgdt => 1, Self::Lldt | Self::Ltr => 1, Self::Invlpg => 1, Self::Rdfsbase | Self::Rdgsbase | Self::Wrfsbase | Self::Wrgsbase => 1,
Self::Rdpmc | Self::Rdpid => 1, Self::Clflush | Self::Clflushopt | Self::Clwb => 1, Self::Prefetch | Self::Prefetchw => 2, Self::Monitor | Self::Monitorx => 3, Self::Mwait | Self::Mwaitx => 2, _ => 0, }
}
pub fn has_side_effects(&self) -> bool {
!matches!(
self,
Self::Cpuid
| Self::Rdtsc
| Self::Rdtscp
| Self::Rdpid
| Self::Sidt
| Self::Sgdt
| Self::Sldt
| Self::Str
)
}
pub fn generate_inline_asm(&self, is_64bit: bool) -> String {
match self {
Self::Cli => "cli".into(),
Self::Sti => "sti".into(),
Self::Hlt => "hlt".into(),
Self::Pause => "pause".into(),
Self::Mfence => "mfence".into(),
Self::Lfence => "lfence".into(),
Self::Sfence => "sfence".into(),
Self::Wbinvd => "wbinvd".into(),
Self::Invd => "invd".into(),
Self::Serialize => "serialize".into(),
Self::Cpuid => "cpuid".into(),
Self::Rdtsc => "rdtsc".into(),
Self::Rdtscp => "rdtscp".into(),
Self::Swapgs => "swapgs".into(),
Self::Invlpg => {
if is_64bit {
"invlpg (%rdi)".into()
} else {
"invlpg (%eax)".into()
}
}
Self::Lidt => {
if is_64bit {
"lidt (%rdi)".into()
} else {
"lidt (%eax)".into()
}
}
Self::Sidt => {
if is_64bit {
"sidt (%rdi)".into()
} else {
"sidt (%eax)".into()
}
}
Self::Lgdt => {
if is_64bit {
"lgdt (%rdi)".into()
} else {
"lgdt (%eax)".into()
}
}
Self::Sgdt => {
if is_64bit {
"sgdt (%rdi)".into()
} else {
"sgdt (%eax)".into()
}
}
Self::Lldt => {
if is_64bit {
"lldt %di".into()
} else {
"lldt %ax".into()
}
}
Self::Sldt => {
if is_64bit {
"sldt %ax".into()
} else {
"sldt %ax".into()
}
}
Self::Ltr => {
if is_64bit {
"ltr %di".into()
} else {
"ltr %ax".into()
}
}
Self::Str => "str %ax".into(),
Self::Inb => {
if is_64bit {
"inb %dx, %al".into()
} else {
"inb %dx, %al".into()
}
}
Self::Outb => "outb %al, %dx".into(),
Self::Inw => "inw %dx, %ax".into(),
Self::Outw => "outw %ax, %dx".into(),
Self::Inl => "inl %dx, %eax".into(),
Self::Outl => "outl %eax, %dx".into(),
Self::Rdmsr => "rdmsr".into(),
Self::Wrmsr => "wrmsr".into(),
Self::Xsetbv => "xsetbv".into(),
Self::Xgetbv => "xgetbv".into(),
Self::Rdfsbase => "rdfsbase %rax".into(),
Self::Rdgsbase => "rdgsbase %rax".into(),
Self::Wrfsbase => "wrfsbase %rax".into(),
Self::Wrgsbase => "wrgsbase %rax".into(),
Self::Rdpmc => "rdpmc".into(),
Self::Rdpid => "rdpid %rax".into(),
Self::Clflush => {
if is_64bit {
"clflush (%rdi)".into()
} else {
"clflush (%eax)".into()
}
}
Self::Clflushopt => {
if is_64bit {
"clflushopt (%rdi)".into()
} else {
"clflushopt (%eax)".into()
}
}
Self::Clwb => {
if is_64bit {
"clwb (%rdi)".into()
} else {
"clwb (%eax)".into()
}
}
Self::Prefetch => "prefetcht0 (%rdi)".into(),
Self::Prefetchw => "prefetchw (%rdi)".into(),
Self::Monitor => "monitor".into(),
Self::Monitorx => "monitorx".into(),
Self::Mwait => "mwait".into(),
Self::Mwaitx => "mwaitx".into(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EmbeddedIntrinsicCategory {
Interrupt,
DescriptorTable,
IoPort,
Msr,
Timestamp,
CpuId,
Cache,
CacheFlush,
Fence,
ControlRegister,
SegmentBase,
PowerManagement,
Misc,
}
#[derive(Debug, Clone, Default)]
pub struct X86IntrinsicsEmbedded {
pub name_map: HashMap<String, EmbeddedX86Intrinsic>,
pub allow_ring0: bool,
pub is_64bit: bool,
pub enabled_categories: Vec<EmbeddedIntrinsicCategory>,
}
impl X86IntrinsicsEmbedded {
pub fn new() -> Self {
let mut name_map = HashMap::new();
let all = [
EmbeddedX86Intrinsic::Cli,
EmbeddedX86Intrinsic::Sti,
EmbeddedX86Intrinsic::Hlt,
EmbeddedX86Intrinsic::Pause,
EmbeddedX86Intrinsic::Lidt,
EmbeddedX86Intrinsic::Sidt,
EmbeddedX86Intrinsic::Lgdt,
EmbeddedX86Intrinsic::Sgdt,
EmbeddedX86Intrinsic::Lldt,
EmbeddedX86Intrinsic::Sldt,
EmbeddedX86Intrinsic::Ltr,
EmbeddedX86Intrinsic::Str,
EmbeddedX86Intrinsic::Inb,
EmbeddedX86Intrinsic::Outb,
EmbeddedX86Intrinsic::Inw,
EmbeddedX86Intrinsic::Outw,
EmbeddedX86Intrinsic::Inl,
EmbeddedX86Intrinsic::Outl,
EmbeddedX86Intrinsic::Rdmsr,
EmbeddedX86Intrinsic::Wrmsr,
EmbeddedX86Intrinsic::Rdtsc,
EmbeddedX86Intrinsic::Rdtscp,
EmbeddedX86Intrinsic::Cpuid,
EmbeddedX86Intrinsic::Invlpg,
EmbeddedX86Intrinsic::Wbinvd,
EmbeddedX86Intrinsic::Invd,
EmbeddedX86Intrinsic::Mfence,
EmbeddedX86Intrinsic::Lfence,
EmbeddedX86Intrinsic::Sfence,
EmbeddedX86Intrinsic::Xsetbv,
EmbeddedX86Intrinsic::Xgetbv,
EmbeddedX86Intrinsic::Rdfsbase,
EmbeddedX86Intrinsic::Rdgsbase,
EmbeddedX86Intrinsic::Wrfsbase,
EmbeddedX86Intrinsic::Wrgsbase,
EmbeddedX86Intrinsic::Swapgs,
EmbeddedX86Intrinsic::Serialize,
EmbeddedX86Intrinsic::Rdpmc,
EmbeddedX86Intrinsic::Rdpid,
EmbeddedX86Intrinsic::Clflush,
EmbeddedX86Intrinsic::Clflushopt,
EmbeddedX86Intrinsic::Clwb,
EmbeddedX86Intrinsic::Prefetch,
EmbeddedX86Intrinsic::Prefetchw,
EmbeddedX86Intrinsic::Monitor,
EmbeddedX86Intrinsic::Monitorx,
EmbeddedX86Intrinsic::Mwait,
EmbeddedX86Intrinsic::Mwaitx,
];
for intrinsic in &all {
name_map.insert(intrinsic.builtin_name().to_string(), *intrinsic);
}
Self {
name_map,
allow_ring0: true,
is_64bit: true,
enabled_categories: Vec::new(),
}
}
pub fn lookup(&self, name: &str) -> Option<EmbeddedX86Intrinsic> {
self.name_map.get(name).copied()
}
pub fn can_use(&self, intrinsic: EmbeddedX86Intrinsic) -> bool {
if intrinsic.requires_ring0() && !self.allow_ring0 {
return false;
}
if intrinsic.requires_64bit() && !self.is_64bit {
return false;
}
if !self.enabled_categories.is_empty()
&& !self.enabled_categories.contains(&intrinsic.category())
{
return false;
}
true
}
pub fn with_ring0(mut self, allow: bool) -> Self {
self.allow_ring0 = allow;
self
}
pub fn with_64bit(mut self, bits64: bool) -> Self {
self.is_64bit = bits64;
self
}
pub fn enable_category(mut self, cat: EmbeddedIntrinsicCategory) -> Self {
self.enabled_categories.push(cat);
self
}
pub fn count(&self) -> usize {
self.name_map.len()
}
pub fn is_empty(&self) -> bool {
self.name_map.is_empty()
}
pub fn generate_header(&self) -> String {
let mut h = String::new();
h.push_str("/* X86 Embedded Intrinsics Header — generated by llvm-native */\n");
h.push_str("#ifndef __X86_EMBEDDED_INTRINSICS_H\n");
h.push_str("#define __X86_EMBEDDED_INTRINSICS_H\n\n");
h.push_str("#ifdef __cplusplus\n");
h.push_str("extern \"C\" {\n");
h.push_str("#endif\n\n");
h.push_str("/* ── Interrupt Control ── */\n");
h.push_str("static inline void __builtin_ia32_cli(void) { __asm__ volatile(\"cli\"); }\n");
h.push_str("static inline void __builtin_ia32_sti(void) { __asm__ volatile(\"sti\"); }\n");
h.push_str("static inline void __builtin_ia32_hlt(void) { __asm__ volatile(\"hlt\"); }\n");
h.push_str(
"static inline void __builtin_ia32_pause(void) { __asm__ volatile(\"pause\"); }\n\n",
);
h.push_str("/* ── I/O Port Access ── */\n");
h.push_str("static inline uint8_t __builtin_ia32_inb(uint16_t port) {\n");
h.push_str(" uint8_t v; __asm__ volatile(\"inb %1, %0\" : \"=a\"(v) : \"Nd\"(port)); return v;\n}\n");
h.push_str("static inline void __builtin_ia32_outb(uint16_t port, uint8_t val) {\n");
h.push_str(" __asm__ volatile(\"outb %0, %1\" :: \"a\"(val), \"Nd\"(port));\n}\n");
h.push_str("static inline uint16_t __builtin_ia32_inw(uint16_t port) {\n");
h.push_str(" uint16_t v; __asm__ volatile(\"inw %1, %0\" : \"=a\"(v) : \"Nd\"(port)); return v;\n}\n");
h.push_str("static inline void __builtin_ia32_outw(uint16_t port, uint16_t val) {\n");
h.push_str(" __asm__ volatile(\"outw %0, %1\" :: \"a\"(val), \"Nd\"(port));\n}\n");
h.push_str("static inline uint32_t __builtin_ia32_inl(uint16_t port) {\n");
h.push_str(" uint32_t v; __asm__ volatile(\"inl %1, %0\" : \"=a\"(v) : \"Nd\"(port)); return v;\n}\n");
h.push_str("static inline void __builtin_ia32_outl(uint16_t port, uint32_t val) {\n");
h.push_str(" __asm__ volatile(\"outl %0, %1\" :: \"a\"(val), \"Nd\"(port));\n}\n\n");
h.push_str("/* ── MSR Access ── */\n");
h.push_str("static inline uint64_t __builtin_ia32_rdmsr(uint32_t msr) {\n");
h.push_str(" uint32_t lo, hi;\n");
h.push_str(" __asm__ volatile(\"rdmsr\" : \"=a\"(lo), \"=d\"(hi) : \"c\"(msr));\n");
h.push_str(" return ((uint64_t)hi << 32) | lo;\n}\n");
h.push_str("static inline void __builtin_ia32_wrmsr(uint32_t msr, uint64_t val) {\n");
h.push_str(" uint32_t lo = val, hi = val >> 32;\n");
h.push_str(" __asm__ volatile(\"wrmsr\" :: \"a\"(lo), \"d\"(hi), \"c\"(msr));\n}\n\n");
h.push_str("/* ── Timestamp Counter ── */\n");
h.push_str("static inline uint64_t __builtin_ia32_rdtsc(void) {\n");
h.push_str(" uint32_t lo, hi;\n");
h.push_str(" __asm__ volatile(\"rdtsc\" : \"=a\"(lo), \"=d\"(hi));\n");
h.push_str(" return ((uint64_t)hi << 32) | lo;\n}\n\n");
h.push_str("/* ── CPUID ── */\n");
h.push_str("static inline void __builtin_ia32_cpuid(uint32_t leaf, uint32_t subleaf,\n");
h.push_str(" uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) {\n");
h.push_str(" __asm__ volatile(\"cpuid\"\n");
h.push_str(" : \"=a\"(*eax), \"=b\"(*ebx), \"=c\"(*ecx), \"=d\"(*edx)\n");
h.push_str(" : \"a\"(leaf), \"c\"(subleaf));\n}\n\n");
h.push_str("/* ── Memory Fences ── */\n");
h.push_str("static inline void __builtin_ia32_mfence(void) { __asm__ volatile(\"mfence\" ::: \"memory\"); }\n");
h.push_str("static inline void __builtin_ia32_lfence(void) { __asm__ volatile(\"lfence\" ::: \"memory\"); }\n");
h.push_str("static inline void __builtin_ia32_sfence(void) { __asm__ volatile(\"sfence\" ::: \"memory\"); }\n\n");
h.push_str("#ifdef __cplusplus\n}\n#endif\n\n");
h.push_str("#endif /* __X86_EMBEDDED_INTRINSICS_H */\n");
h
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InterruptGateType {
TaskGate = 0x5,
InterruptGate16 = 0x6,
TrapGate16 = 0x7,
InterruptGate32 = 0xE,
TrapGate32 = 0xF,
}
#[derive(Debug, Clone, Copy)]
pub struct IdtEntry {
pub offset_low: u16,
pub selector: u16,
pub ist: u8,
pub gate_type: InterruptGateType,
pub dpl: u8,
pub present: bool,
pub offset_mid: u16,
pub offset_high: u32,
pub reserved: u32,
}
impl IdtEntry {
pub fn new_64(
handler_addr: u64,
selector: u16,
gate_type: InterruptGateType,
dpl: u8,
ist: u8,
) -> Self {
Self {
offset_low: handler_addr as u16,
selector,
ist: ist & 0x7,
gate_type,
dpl: dpl & 0x3,
present: true,
offset_mid: ((handler_addr >> 16) & 0xFFFF) as u16,
offset_high: (handler_addr >> 32) as u32,
reserved: 0,
}
}
pub fn new_32(handler_addr: u32, selector: u16, gate_type: InterruptGateType, dpl: u8) -> Self {
Self {
offset_low: handler_addr as u16,
selector,
ist: 0,
gate_type,
dpl: dpl & 0x3,
present: true,
offset_mid: ((handler_addr >> 16) & 0xFFFF) as u16,
offset_high: 0,
reserved: 0,
}
}
pub fn encode_64(&self) -> [u8; 16] {
let mut buf = [0u8; 16];
let word0: u64 = (self.offset_low as u64)
| ((self.selector as u64) << 16)
| ((self.ist as u64) << 32)
| (((self.gate_type as u64) & 0xF) << 40)
| (((self.dpl as u64) & 0x3) << 45)
| ((self.present as u64) << 47)
| ((self.offset_mid as u64) << 48);
let word1: u64 = (self.offset_high as u64) | ((self.reserved as u64) << 32);
buf[0..8].copy_from_slice(&word0.to_le_bytes());
buf[8..16].copy_from_slice(&word1.to_le_bytes());
buf
}
pub fn encode_32(&self) -> [u8; 8] {
let mut buf = [0u8; 8];
let word: u64 = (self.offset_low as u64)
| ((self.selector as u64) << 16)
| ((((self.gate_type as u64) & 0xF) | 0x80) << 40) | (((self.dpl as u64) & 0x3) << 45)
| (((self.present as u64) << 47))
| ((self.offset_mid as u64) << 48);
buf.copy_from_slice(&word.to_le_bytes());
buf
}
}
impl Default for IdtEntry {
fn default() -> Self {
Self {
offset_low: 0,
selector: 0,
ist: 0,
gate_type: InterruptGateType::InterruptGate32,
dpl: 0,
present: true,
offset_mid: 0,
offset_high: 0,
reserved: 0,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct IdtDescriptor {
pub limit: u16,
pub base: u64,
}
impl IdtDescriptor {
pub fn new(base: u64, num_entries: u16) -> Self {
Self {
limit: num_entries.saturating_mul(16).saturating_sub(1),
base,
}
}
pub fn encode_64(&self) -> [u8; 10] {
let mut buf = [0u8; 10];
buf[0..2].copy_from_slice(&self.limit.to_le_bytes());
buf[2..10].copy_from_slice(&self.base.to_le_bytes());
buf
}
pub fn encode_32(&self) -> [u8; 6] {
let mut buf = [0u8; 6];
buf[0..2].copy_from_slice(&self.limit.to_le_bytes());
buf[2..6].copy_from_slice(&(self.base as u32).to_le_bytes());
buf
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExceptionVector {
DivideError = 0,
Debug = 1,
Nmi = 2,
Breakpoint = 3,
Overflow = 4,
BoundRange = 5,
InvalidOpcode = 6,
DeviceNotAvailable = 7,
DoubleFault = 8,
CoprocessorSegmentOverrun = 9,
InvalidTss = 10,
SegmentNotPresent = 11,
StackSegmentFault = 12,
GeneralProtectionFault = 13,
PageFault = 14,
X87FloatingPoint = 16,
AlignmentCheck = 17,
MachineCheck = 18,
SimdFloatingPoint = 19,
Virtualization = 20,
ControlProtection = 21,
HypervisorInjection = 28,
VmmCommunication = 29,
Security = 30,
}
impl ExceptionVector {
pub fn number(&self) -> u8 {
*self as u8
}
pub fn name(&self) -> &'static str {
match self {
Self::DivideError => "#DE",
Self::Debug => "#DB",
Self::Nmi => "NMI",
Self::Breakpoint => "#BP",
Self::Overflow => "#OF",
Self::BoundRange => "#BR",
Self::InvalidOpcode => "#UD",
Self::DeviceNotAvailable => "#NM",
Self::DoubleFault => "#DF",
Self::CoprocessorSegmentOverrun => "#CSO",
Self::InvalidTss => "#TS",
Self::SegmentNotPresent => "#NP",
Self::StackSegmentFault => "#SS",
Self::GeneralProtectionFault => "#GP",
Self::PageFault => "#PF",
Self::X87FloatingPoint => "#MF",
Self::AlignmentCheck => "#AC",
Self::MachineCheck => "#MC",
Self::SimdFloatingPoint => "#XM",
Self::Virtualization => "#VE",
Self::ControlProtection => "#CP",
Self::HypervisorInjection => "#HV",
Self::VmmCommunication => "#VC",
Self::Security => "#SX",
}
}
pub fn has_error_code(&self) -> bool {
matches!(
self,
Self::DoubleFault
| Self::InvalidTss
| Self::SegmentNotPresent
| Self::StackSegmentFault
| Self::GeneralProtectionFault
| Self::PageFault
| Self::AlignmentCheck
| Self::ControlProtection
)
}
pub fn description(&self) -> &'static str {
match self {
Self::DivideError => "Division by zero",
Self::Debug => "Debug exception",
Self::Nmi => "Non-maskable interrupt",
Self::Breakpoint => "Breakpoint (INT3)",
Self::Overflow => "Overflow (INTO)",
Self::BoundRange => "BOUND range exceeded",
Self::InvalidOpcode => "Invalid opcode",
Self::DeviceNotAvailable => "Device not available (no math coprocessor)",
Self::DoubleFault => "Double fault",
Self::CoprocessorSegmentOverrun => "Coprocessor segment overrun",
Self::InvalidTss => "Invalid TSS",
Self::SegmentNotPresent => "Segment not present",
Self::StackSegmentFault => "Stack-segment fault",
Self::GeneralProtectionFault => "General protection fault",
Self::PageFault => "Page fault",
Self::X87FloatingPoint => "x87 FPU floating-point error",
Self::AlignmentCheck => "Alignment check",
Self::MachineCheck => "Machine check",
Self::SimdFloatingPoint => "SIMD floating-point exception",
Self::Virtualization => "Virtualization exception",
Self::ControlProtection => "Control-flow protection exception",
Self::HypervisorInjection => "Hypervisor injection exception",
Self::VmmCommunication => "VMM communication exception",
Self::Security => "Security exception",
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct InterruptStackFrame {
pub instruction_pointer: u64,
pub code_segment: u64,
pub rflags: u64,
pub stack_pointer: u64,
pub stack_segment: u64,
}
impl InterruptStackFrame {
pub fn new(ip: u64, cs: u64, flags: u64) -> Self {
Self {
instruction_pointer: ip,
code_segment: cs,
rflags: flags,
stack_pointer: 0,
stack_segment: 0,
}
}
}
#[derive(Debug, Clone)]
pub struct X86InterruptHandlers {
pub idt_enabled: bool,
pub entries: Vec<Option<IdtEntry>>,
pub idt_base: u64,
pub code_selector: u16,
pub ist_entries: [u8; 8],
pub generate_wrappers: bool,
pub default_dpl: u8,
pub syscall_handler: Option<u64>,
pub syscall_msr_star: Option<u64>,
pub syscall_msr_lstar: Option<u64>,
pub syscall_msr_cstar: Option<u64>,
pub syscall_msr_sfmask: Option<u64>,
}
impl X86InterruptHandlers {
pub fn new() -> Self {
let mut entries = Vec::with_capacity(256);
entries.resize(256, None);
Self {
idt_enabled: true,
entries,
idt_base: 0,
code_selector: 0x08,
ist_entries: [0; 8],
generate_wrappers: true,
default_dpl: 0,
syscall_handler: None,
syscall_msr_star: None,
syscall_msr_lstar: None,
syscall_msr_cstar: None,
syscall_msr_sfmask: None,
}
}
pub fn register_exception(&mut self, vector: ExceptionVector, handler_addr: u64) {
let idx = vector.number() as usize;
if idx < 256 {
self.entries[idx] = Some(IdtEntry::new_64(
handler_addr,
self.code_selector,
InterruptGateType::InterruptGate32,
self.default_dpl,
0,
));
}
}
pub fn register_irq(&mut self, vector: u8, handler_addr: u64) {
let idx = vector as usize;
if idx < 256 {
self.entries[idx] = Some(IdtEntry::new_64(
handler_addr,
self.code_selector,
InterruptGateType::InterruptGate32,
self.default_dpl,
0,
));
}
}
pub fn register_with_ist(&mut self, vector: u8, handler_addr: u64, ist: u8, dpl: u8) {
let idx = vector as usize;
if idx < 256 {
self.entries[idx] = Some(IdtEntry::new_64(
handler_addr,
self.code_selector,
InterruptGateType::InterruptGate32,
dpl,
ist,
));
}
}
pub fn generate_idt_asm(&self) -> Option<String> {
if !self.idt_enabled {
return None;
}
let mut asm = String::new();
asm.push_str("/* Interrupt Descriptor Table — generated by llvm-native */\n\n");
asm.push_str(" .section .data.idt\n");
asm.push_str(" .align 16\n");
asm.push_str(" .global idt\n");
asm.push_str("idt:\n");
for (i, entry) in self.entries.iter().enumerate() {
if let Some(e) = entry {
let enc = e.encode_64();
asm.push_str(&format!(
" /* Vector {}: 0x{:016X} 0x{:016X} */\n",
i,
u64::from_le_bytes(enc[0..8].try_into().unwrap()),
u64::from_le_bytes(enc[8..16].try_into().unwrap()),
));
asm.push_str(&format!(
" .quad 0x{:016X}\n",
u64::from_le_bytes(enc[0..8].try_into().unwrap()),
));
asm.push_str(&format!(
" .quad 0x{:016X}\n",
u64::from_le_bytes(enc[8..16].try_into().unwrap()),
));
} else {
asm.push_str(" .quad 0\n .quad 0\n");
}
}
asm.push_str("\n .align 16\n");
asm.push_str(" .global idt_descriptor\n");
asm.push_str("idt_descriptor:\n");
let desc = IdtDescriptor::new(0, 256);
asm.push_str(&format!(" .short {}\n", desc.limit));
asm.push_str(" .quad idt\n\n");
asm.push_str("/* Install IDT */\n");
asm.push_str(" .section .text\n");
asm.push_str(" .global install_idt\n");
asm.push_str("install_idt:\n");
asm.push_str(" lea idt_descriptor(%rip), %rax\n");
asm.push_str(" lidt (%rax)\n");
asm.push_str(" ret\n");
Some(asm)
}
pub fn generate_isr_wrapper(&self, vector: u8, has_error_code: bool) -> String {
let mut asm = String::new();
asm.push_str(&format!(
"/* ISR wrapper for vector {} — generated by llvm-native */\n",
vector
));
asm.push_str(&format!(" .global isr{}\n", vector));
asm.push_str(&format!(" .type isr{}, @function\n", vector));
asm.push_str(&format!("isr{}:\n", vector));
if !has_error_code {
asm.push_str(" pushq $0 /* dummy error code */\n");
}
asm.push_str(" pushq %rax\n");
asm.push_str(" pushq %rbx\n");
asm.push_str(" pushq %rcx\n");
asm.push_str(" pushq %rdx\n");
asm.push_str(" pushq %rsi\n");
asm.push_str(" pushq %rdi\n");
asm.push_str(" pushq %rbp\n");
asm.push_str(" pushq %r8\n");
asm.push_str(" pushq %r9\n");
asm.push_str(" pushq %r10\n");
asm.push_str(" pushq %r11\n");
asm.push_str(" pushq %r12\n");
asm.push_str(" pushq %r13\n");
asm.push_str(" pushq %r14\n");
asm.push_str(" pushq %r15\n");
asm.push_str(&format!(
" movq ${}, %rdi /* vector number */\n",
vector
));
asm.push_str(" movq %rsp, %rsi /* pointer to saved regs */\n");
asm.push_str(&format!(" call isr{}_handler\n", vector));
asm.push_str(" popq %r15\n");
asm.push_str(" popq %r14\n");
asm.push_str(" popq %r13\n");
asm.push_str(" popq %r12\n");
asm.push_str(" popq %r11\n");
asm.push_str(" popq %r10\n");
asm.push_str(" popq %r9\n");
asm.push_str(" popq %r8\n");
asm.push_str(" popq %rbp\n");
asm.push_str(" popq %rdi\n");
asm.push_str(" popq %rsi\n");
asm.push_str(" popq %rdx\n");
asm.push_str(" popq %rcx\n");
asm.push_str(" popq %rbx\n");
asm.push_str(" popq %rax\n");
asm.push_str(" addq $8, %rsp /* skip error code */\n");
asm.push_str(" iretq\n");
asm
}
pub fn generate_page_fault_handler_c(&self) -> String {
let mut c = String::new();
c.push_str("/* Page Fault Handler — generated by llvm-native */\n\n");
c.push_str("#include <stdint.h>\n\n");
c.push_str("struct interrupt_frame {\n");
c.push_str(" uint64_t r15, r14, r13, r12, r11, r10, r9, r8;\n");
c.push_str(" uint64_t rbp, rdi, rsi, rdx, rcx, rbx, rax;\n");
c.push_str(" uint64_t error_code;\n");
c.push_str(" uint64_t rip, cs, rflags, rsp, ss;\n");
c.push_str("};\n\n");
c.push_str("void isr14_handler(uint64_t vector, struct interrupt_frame *frame) {\n");
c.push_str(" uint64_t fault_addr;\n");
c.push_str(" __asm__ volatile(\"movq %%cr2, %0\" : \"=r\"(fault_addr));\n");
c.push_str(" \n");
c.push_str(" /* Decode error code */\n");
c.push_str(" uint64_t err = frame->error_code;\n");
c.push_str(" int present = (err & 0x01) != 0; /* P: page was present */\n");
c.push_str(" int write = (err & 0x02) != 0; /* W/R: write access */\n");
c.push_str(" int user = (err & 0x04) != 0; /* U/S: user mode */\n");
c.push_str(" int reserved = (err & 0x08) != 0; /* RSVD: reserved bit set */\n");
c.push_str(" int exec = (err & 0x10) != 0; /* I/D: instruction fetch */\n");
c.push_str(" \n");
c.push_str(" /* Handle the fault */\n");
c.push_str(" panic(\"Page fault at 0x%lx, error=0x%lx, rip=0x%lx\",\n");
c.push_str(" fault_addr, err, frame->rip);\n");
c.push_str("}\n");
c
}
pub fn generate_syscall_setup_asm(&self) -> String {
let mut asm = String::new();
asm.push_str("/* SYSCALL/SYSENTER Setup — generated by llvm-native */\n\n");
asm.push_str(" .section .text\n");
asm.push_str(" .global syscall_init\n");
asm.push_str("syscall_init:\n");
if let Some(star) = self.syscall_msr_star {
asm.push_str(&format!(" /* Write IA32_STAR (0xC0000081) */\n"));
asm.push_str(&format!(" mov $0xC0000081, %ecx\n"));
asm.push_str(&format!(" mov $0x{:X}, %eax\n", star as u32));
asm.push_str(&format!(" mov $0x{:X}, %edx\n", (star >> 32) as u32));
asm.push_str(" wrmsr\n");
}
if let Some(lstar) = self.syscall_msr_lstar {
asm.push_str(&format!(" /* Write IA32_LSTAR (0xC0000082) */\n"));
asm.push_str(&format!(" mov $0xC0000082, %ecx\n"));
asm.push_str(&format!(" mov $0x{:X}, %eax\n", lstar as u32));
asm.push_str(&format!(" mov $0x{:X}, %edx\n", (lstar >> 32) as u32));
asm.push_str(" wrmsr\n");
}
if let Some(cstar) = self.syscall_msr_cstar {
asm.push_str(&format!(" /* Write IA32_CSTAR (0xC0000083) */\n"));
asm.push_str(&format!(" mov $0xC0000083, %ecx\n"));
asm.push_str(&format!(" mov $0x{:X}, %eax\n", cstar as u32));
asm.push_str(&format!(" mov $0x{:X}, %edx\n", (cstar >> 32) as u32));
asm.push_str(" wrmsr\n");
}
if let Some(sfmask) = self.syscall_msr_sfmask {
asm.push_str(&format!(" /* Write IA32_FMASK (0xC0000084) */\n"));
asm.push_str(&format!(" mov $0xC0000084, %ecx\n"));
asm.push_str(&format!(" mov $0x{:X}, %eax\n", sfmask as u32));
asm.push_str(&format!(" mov $0x{:X}, %edx\n", (sfmask >> 32) as u32));
asm.push_str(" wrmsr\n");
}
asm.push_str(" /* Enable SYSCALL/SYSRET by setting EFER.SCE (bit 0) */\n");
asm.push_str(" mov $0xC0000080, %ecx\n");
asm.push_str(" rdmsr\n");
asm.push_str(" or $1, %eax\n");
asm.push_str(" wrmsr\n");
asm.push_str(" ret\n");
asm
}
pub fn with_syscall_handler(mut self, handler_addr: u64, star: u64, lstar: u64) -> Self {
self.syscall_handler = Some(handler_addr);
self.syscall_msr_star = Some(star);
self.syscall_msr_lstar = Some(lstar);
self
}
pub fn with_code_selector(mut self, sel: u16) -> Self {
self.code_selector = sel;
self
}
pub fn with_idt_base(mut self, base: u64) -> Self {
self.idt_base = base;
self
}
pub fn handler_count(&self) -> usize {
self.entries.iter().filter(|e| e.is_some()).count()
}
pub fn has_handler(&self, vector: u8) -> bool {
self.entries
.get(vector as usize)
.map_or(false, |e| e.is_some())
}
}
impl Default for X86InterruptHandlers {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy)]
pub struct PageTableEntry {
pub raw: u64,
}
impl PageTableEntry {
pub fn new(physical_address: u64, flags: PageFlags) -> Self {
let addr = physical_address & 0x000F_FFFF_FFFF_F000; let mut raw = addr | flags.bits();
if flags.present {
raw |= PTE_PRESENT;
}
Self { raw }
}
pub fn empty() -> Self {
Self { raw: 0 }
}
pub fn physical_address(&self) -> u64 {
self.raw & 0x000F_FFFF_FFFF_F000
}
pub fn is_present(&self) -> bool {
(self.raw & PTE_PRESENT) != 0
}
pub fn is_huge(&self) -> bool {
(self.raw & PTE_HUGE) != 0
}
pub fn set_address(&mut self, addr: u64) {
self.raw = (self.raw & !0x000F_FFFF_FFFF_F000) | (addr & 0x000F_FFFF_FFFF_F000);
}
pub fn set_flags(&mut self, flags: PageFlags) {
self.raw = (self.raw & 0x000F_FFFF_FFFF_F000) | flags.bits();
if flags.present {
self.raw |= PTE_PRESENT;
}
}
}
pub const PTE_PRESENT: u64 = 1 << 0;
pub const PTE_WRITABLE: u64 = 1 << 1;
pub const PTE_USER: u64 = 1 << 2;
pub const PTE_PWT: u64 = 1 << 3; pub const PTE_PCD: u64 = 1 << 4; pub const PTE_ACCESSED: u64 = 1 << 5;
pub const PTE_DIRTY: u64 = 1 << 6;
pub const PTE_HUGE: u64 = 1 << 7; pub const PTE_GLOBAL: u64 = 1 << 8;
pub const PTE_NX: u64 = 1 << 63;
pub const PTE_PAT_4K: u64 = 1 << 7;
pub const PTE_PAT_LARGE: u64 = 1 << 12;
#[derive(Debug, Clone, Copy, Default)]
pub struct PageFlags {
pub present: bool,
pub writable: bool,
pub user_accessible: bool,
pub write_through: bool,
pub cache_disable: bool,
pub accessed: bool,
pub dirty: bool,
pub global: bool,
pub no_execute: bool,
pub huge_page: bool,
pub pat: bool,
}
impl PageFlags {
pub fn kernel_rw() -> Self {
Self {
present: true,
writable: true,
user_accessible: false,
write_through: false,
cache_disable: false,
accessed: false,
dirty: false,
global: false,
no_execute: false,
huge_page: false,
pat: false,
}
}
pub fn user_rw() -> Self {
Self {
present: true,
writable: true,
user_accessible: true,
..Self::kernel_rw()
}
}
pub fn kernel_ro() -> Self {
Self {
writable: false,
..Self::kernel_rw()
}
}
pub fn kernel_rw_nx() -> Self {
Self {
no_execute: true,
..Self::kernel_rw()
}
}
pub fn kernel_rx() -> Self {
Self {
writable: false,
..Self::kernel_rw()
}
}
pub fn bits(&self) -> u64 {
let mut b = 0u64;
if self.writable {
b |= PTE_WRITABLE;
}
if self.user_accessible {
b |= PTE_USER;
}
if self.write_through {
b |= PTE_PWT;
}
if self.cache_disable {
b |= PTE_PCD;
}
if self.accessed {
b |= PTE_ACCESSED;
}
if self.dirty {
b |= PTE_DIRTY;
}
if self.global {
b |= PTE_GLOBAL;
}
if self.no_execute {
b |= PTE_NX;
}
if self.huge_page {
b |= PTE_HUGE;
}
if self.pat && self.huge_page {
b |= PTE_PAT_LARGE;
} else if self.pat {
b |= PTE_PAT_4K;
}
b
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PageTableLevel {
Pml4,
Pdpt,
Pd,
Pt,
}
impl PageTableLevel {
pub fn entries_per_table(&self) -> usize {
512
}
pub fn index_shift(&self) -> u8 {
match self {
Self::Pml4 => 39,
Self::Pdpt => 30,
Self::Pd => 21,
Self::Pt => 12,
}
}
pub fn index_of(&self, vaddr: u64) -> usize {
((vaddr >> self.index_shift()) & 0x1FF) as usize
}
pub fn page_size(&self) -> u64 {
match self {
Self::Pml4 => 512 * 1024 * 1024 * 1024, Self::Pdpt => 1024 * 1024 * 1024, Self::Pd => 2 * 1024 * 1024, Self::Pt => 4096, }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PagingLevel {
Level4,
Level5,
}
impl PagingLevel {
pub fn va_bits(&self) -> u8 {
match self {
Self::Level4 => 48,
Self::Level5 => 57,
}
}
pub fn pa_bits(&self) -> u8 {
52
}
pub fn canonical_mask(&self) -> u64 {
let va_bits = self.va_bits();
if va_bits >= 64 {
0
} else {
!((1u64 << (va_bits - 1)) - 1)
}
}
}
#[derive(Debug, Clone)]
pub struct X86PagingSupport {
pub enabled: bool,
pub bitness: X86Bitness,
pub paging_level: PagingLevel,
pub memory_map: Vec<PhysicalMemoryRange>,
pub identity_map_bytes: u64,
pub recursive_map: Option<u64>, pub kernel_vbase: u64,
pub kernel_pload: u64,
pub enable_nx: bool,
pub enable_global_pages: bool,
}
#[derive(Debug, Clone, Copy)]
pub struct PhysicalMemoryRange {
pub base_address: u64,
pub length: u64,
pub range_type: MemoryRangeType,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MemoryRangeType {
Available,
Reserved,
AcpiReclaim,
AcpiNvs,
BadMemory,
KernelCode,
KernelData,
KernelStack,
Mmio,
}
impl X86PagingSupport {
pub fn new() -> Self {
Self {
enabled: true,
bitness: X86Bitness::Bits64,
paging_level: PagingLevel::Level4,
memory_map: Vec::new(),
identity_map_bytes: 0x200000, recursive_map: Some(510), kernel_vbase: 0xFFFF_8000_0000_0000, kernel_pload: 0x100000, enable_nx: true,
enable_global_pages: true,
}
}
pub fn new_32bit_pae() -> Self {
Self {
enabled: true,
bitness: X86Bitness::Bits32,
paging_level: PagingLevel::Level4, memory_map: Vec::new(),
identity_map_bytes: 0x400000,
recursive_map: None, kernel_vbase: 0xC000_0000, kernel_pload: 0x100000,
enable_nx: false, enable_global_pages: true,
}
}
pub fn add_memory_range(mut self, base: u64, len: u64, ty: MemoryRangeType) -> Self {
self.memory_map.push(PhysicalMemoryRange {
base_address: base,
length: len,
range_type: ty,
});
self
}
pub fn generate_page_tables_asm(&self) -> Option<String> {
if !self.enabled || self.bitness != X86Bitness::Bits64 {
return None;
}
let mut asm = String::new();
asm.push_str("/* Page Table Initialization — generated by llvm-native */\n\n");
asm.push_str(" .section .data.pagetables\n");
asm.push_str(" .align 4096\n\n");
asm.push_str(" .global pml4\n");
asm.push_str("pml4:\n");
asm.push_str(" .space 4096, 0\n\n");
asm.push_str(" .global pdpt\n");
asm.push_str("pdpt:\n");
asm.push_str(" .space 4096, 0\n\n");
asm.push_str(" .global pd\n");
asm.push_str("pd:\n");
asm.push_str(" .space 4096, 0\n\n");
asm.push_str(" .global pt_identity\n");
asm.push_str("pt_identity:\n");
asm.push_str(" .space 4096, 0\n\n");
asm.push_str("/* Initialize page tables */\n");
asm.push_str(" .section .text\n");
asm.push_str(" .global enable_paging\n");
asm.push_str("enable_paging:\n");
asm.push_str(" /* Map PML4[0] -> PDPT */\n");
asm.push_str(" lea pdpt(%rip), %rax\n");
asm.push_str(" orq $0x03, %rax /* present + writable */\n");
asm.push_str(" movq %rax, pml4(%rip)\n");
asm.push_str(" /* Map PDPT[0] -> PD */\n");
asm.push_str(" lea pd(%rip), %rax\n");
asm.push_str(" orq $0x03, %rax\n");
asm.push_str(" movq %rax, pdpt(%rip)\n");
asm.push_str(" /* Identity-map first 2 MB using 2 MB pages */\n");
asm.push_str(" movq $0x00000083, %rax /* 0x0, huge, present+writable */\n");
asm.push_str(" movq %rax, pd(%rip)\n");
asm.push_str(" movq $0x00200083, %rax /* 2 MB */\n");
asm.push_str(" movq %rax, pd+8(%rip)\n");
asm.push_str(" movq $0x00400083, %rax /* 4 MB */\n");
asm.push_str(" movq %rax, pd+16(%rip)\n");
asm.push_str(" movq $0x00600083, %rax /* 6 MB */\n");
asm.push_str(" movq %rax, pd+24(%rip)\n");
asm.push_str(" /* Load PML4 into CR3 */\n");
asm.push_str(" lea pml4(%rip), %rax\n");
asm.push_str(" movq %rax, %cr3\n");
asm.push_str(" /* Enable PAE (if not already) */\n");
asm.push_str(" movq %cr4, %rax\n");
asm.push_str(" orq $0x20, %rax /* CR4.PAE */\n");
asm.push_str(" movq %rax, %cr4\n");
asm.push_str(" /* Enable Long Mode in EFER */\n");
asm.push_str(" movl $0xC0000080, %ecx\n");
asm.push_str(" rdmsr\n");
asm.push_str(" orl $0x100, %eax /* EFER.LME */\n");
asm.push_str(" wrmsr\n");
asm.push_str(" /* Enable paging */\n");
asm.push_str(" movq %cr0, %rax\n");
asm.push_str(" orq $0x80000000, %rax /* CR0.PG */\n");
asm.push_str(" movq %rax, %cr0\n");
asm.push_str(" ret\n");
Some(asm)
}
pub fn generate_page_tables_c(&self) -> String {
let mut c = String::new();
c.push_str("/* Page Table Initialization — generated by llvm-native */\n\n");
c.push_str("#include <stdint.h>\n");
c.push_str("#include <string.h>\n\n");
c.push_str("#define PAGE_PRESENT (1ULL << 0)\n");
c.push_str("#define PAGE_WRITABLE (1ULL << 1)\n");
c.push_str("#define PAGE_USER (1ULL << 2)\n");
c.push_str("#define PAGE_HUGE (1ULL << 7)\n");
c.push_str("#define PAGE_NX (1ULL << 63)\n\n");
c.push_str("static uint64_t pml4[512] __attribute__((aligned(4096)));\n");
c.push_str("static uint64_t pdpt[512] __attribute__((aligned(4096)));\n");
c.push_str("static uint64_t pd[512] __attribute__((aligned(4096)));\n");
c.push_str("static uint64_t pt[512] __attribute__((aligned(4096)));\n\n");
c.push_str("void init_paging(void) {\n");
c.push_str(" memset(pml4, 0, sizeof(pml4));\n");
c.push_str(" memset(pdpt, 0, sizeof(pdpt));\n");
c.push_str(" memset(pd, 0, sizeof(pd));\n");
c.push_str(" memset(pt, 0, sizeof(pt));\n");
c.push_str(" /* PML4[0] -> PDPT */\n");
c.push_str(" pml4[0] = ((uint64_t)(uintptr_t)pdpt) | PAGE_PRESENT | PAGE_WRITABLE;\n");
c.push_str(" /* PDPT[0] -> PD */\n");
c.push_str(" pdpt[0] = ((uint64_t)(uintptr_t)pd) | PAGE_PRESENT | PAGE_WRITABLE;\n");
c.push_str(" /* Identity-map first 2 MB with 2 MB pages */\n");
c.push_str(" for (int i = 0; i < 4; i++) {\n");
c.push_str(
" pd[i] = (i * 0x200000ULL) | PAGE_PRESENT | PAGE_WRITABLE | PAGE_HUGE;\n",
);
c.push_str(" }\n");
c.push_str(" /* Load CR3 */\n");
c.push_str(
" __asm__ volatile(\"movq %0, %%cr3\" :: \"r\"((uint64_t)(uintptr_t)pml4));\n",
);
c.push_str("}\n");
c
}
pub fn virtual_to_physical(&self, pages: &[PageTableEntry], vaddr: u64) -> Option<u64> {
let idx_pml4 = ((vaddr >> 39) & 0x1FF) as usize;
let idx_pdpt = ((vaddr >> 30) & 0x1FF) as usize;
let idx_pd = ((vaddr >> 21) & 0x1FF) as usize;
let idx_pt = ((vaddr >> 12) & 0x1FF) as usize;
let offset = vaddr & 0xFFF;
if idx_pml4 >= pages.len() || !pages[idx_pml4].is_present() {
return None;
}
let phys = pages[idx_pml4].physical_address() + offset;
Some(phys)
}
pub fn with_identity_map(mut self, bytes: u64) -> Self {
self.identity_map_bytes = bytes;
self
}
pub fn with_recursive_map(mut self, idx: u64) -> Self {
self.recursive_map = Some(idx);
self
}
pub fn with_kernel_vbase(mut self, vbase: u64) -> Self {
self.kernel_vbase = vbase;
self
}
pub fn with_kernel_pload(mut self, pload: u64) -> Self {
self.kernel_pload = pload;
self
}
pub fn with_nx(mut self, enable: bool) -> Self {
self.enable_nx = enable;
self
}
}
impl Default for X86PagingSupport {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy)]
pub struct AcpiRsdp {
pub signature: [u8; 8],
pub checksum: u8,
pub oem_id: [u8; 6],
pub revision: u8,
pub rsdt_address: u32,
pub length: u32,
pub xsdt_address: u64,
pub extended_checksum: u8,
pub reserved: [u8; 3],
}
impl AcpiRsdp {
pub fn validate_checksum(&self) -> bool {
let bytes = self.to_bytes_v1();
let sum: u8 = bytes.iter().fold(0u8, |a, &b| a.wrapping_add(b));
sum == 0
}
pub fn to_bytes_v1(&self) -> [u8; 20] {
let mut buf = [0u8; 20];
buf[0..8].copy_from_slice(&self.signature);
buf[8] = self.checksum;
buf[9..15].copy_from_slice(&self.oem_id);
buf[15] = self.revision;
buf[16..20].copy_from_slice(&self.rsdt_address.to_le_bytes());
buf
}
pub fn to_bytes_v2(&self) -> [u8; 36] {
let mut buf = [0u8; 36];
buf[0..8].copy_from_slice(&self.signature);
buf[8] = self.checksum;
buf[9..15].copy_from_slice(&self.oem_id);
buf[15] = self.revision;
buf[16..20].copy_from_slice(&self.rsdt_address.to_le_bytes());
buf[20..24].copy_from_slice(&self.length.to_le_bytes());
buf[24..32].copy_from_slice(&self.xsdt_address.to_le_bytes());
buf[32] = self.extended_checksum;
buf[33..36].copy_from_slice(&self.reserved);
buf
}
}
impl Default for AcpiRsdp {
fn default() -> Self {
Self {
signature: *b"RSD PTR ",
checksum: 0,
oem_id: [0; 6],
revision: 2,
rsdt_address: 0,
length: 36,
xsdt_address: 0,
extended_checksum: 0,
reserved: [0; 3],
}
}
}
#[derive(Debug, Clone)]
pub struct AcpiSdtHeader {
pub signature: [u8; 4],
pub length: u32,
pub revision: u8,
pub checksum: u8,
pub oem_id: [u8; 6],
pub oem_table_id: [u8; 8],
pub oem_revision: u32,
pub creator_id: u32,
pub creator_revision: u32,
}
impl AcpiSdtHeader {
pub fn parse(data: &[u8]) -> Option<Self> {
if data.len() < 36 {
return None;
}
let mut sig = [0u8; 4];
sig.copy_from_slice(&data[0..4]);
let mut oem = [0u8; 6];
oem.copy_from_slice(&data[10..16]);
let mut tid = [0u8; 8];
tid.copy_from_slice(&data[16..24]);
Some(Self {
signature: sig,
length: u32::from_le_bytes(data[4..8].try_into().ok()?),
revision: data[8],
checksum: data[9],
oem_id: oem,
oem_table_id: tid,
oem_revision: u32::from_le_bytes(data[24..28].try_into().ok()?),
creator_id: u32::from_le_bytes(data[28..32].try_into().ok()?),
creator_revision: u32::from_le_bytes(data[32..36].try_into().ok()?),
})
}
pub fn signature_str(&self) -> String {
String::from_utf8_lossy(&self.signature).to_string()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AcpiTableKind {
Dsdt,
Ssdt,
Fadt,
Facs,
Madt,
Sbst,
Ecdt,
Hpet,
Mcfg,
Spcr,
Dbgp,
Srat,
Slit,
Cpep,
Msch,
Bgrt,
Unknown([u8; 4]),
}
impl AcpiTableKind {
pub fn from_signature(sig: &[u8; 4]) -> Self {
match sig {
b"DSDT" => Self::Dsdt,
b"SSDT" => Self::Ssdt,
b"FACP" => Self::Fadt,
b"FACS" => Self::Facs,
b"APIC" => Self::Madt,
b"SBST" => Self::Sbst,
b"ECDT" => Self::Ecdt,
b"HPET" => Self::Hpet,
b"MCFG" => Self::Mcfg,
b"SPCR" => Self::Spcr,
b"DBGP" => Self::Dbgp,
b"SRAT" => Self::Srat,
b"SLIT" => Self::Slit,
b"CPEP" => Self::Cpep,
b"MSCH" => Self::Msch,
b"BGRT" => Self::Bgrt,
_ => Self::Unknown(*sig),
}
}
pub fn as_str(&self) -> &'static str {
match self {
Self::Dsdt => "DSDT",
Self::Ssdt => "SSDT",
Self::Fadt => "FADT",
Self::Facs => "FACS",
Self::Madt => "MADT",
Self::Sbst => "SBST",
Self::Ecdt => "ECDT",
Self::Hpet => "HPET",
Self::Mcfg => "MCFG",
Self::Spcr => "SPCR",
Self::Dbgp => "DBGP",
Self::Srat => "SRAT",
Self::Slit => "SLIT",
Self::Cpep => "CPEP",
Self::Msch => "MSCH",
Self::Bgrt => "BGRT",
Self::Unknown(_) => "UNKN",
}
}
}
#[derive(Debug, Clone)]
pub struct SmbiosEntryPoint {
pub anchor: [u8; 4],
pub checksum: u8,
pub length: u8,
pub major_version: u8,
pub minor_version: u8,
pub max_structure_size: u16,
pub entry_point_revision: u8,
pub intermediate_anchor: [u8; 5],
pub structure_table_address: u64,
pub structure_table_length: u32,
}
impl SmbiosEntryPoint {
pub fn validate(&self) -> bool {
let anchor_str = std::str::from_utf8(&self.anchor).unwrap_or("");
anchor_str == "_SM_" || anchor_str == "_SM3_"
}
pub fn anchor_str(&self) -> String {
String::from_utf8_lossy(&self.anchor).to_string()
}
pub fn new_v3() -> Self {
Self {
anchor: *b"_SM_",
checksum: 0,
length: 0x18,
major_version: 3,
minor_version: 0,
max_structure_size: 0,
entry_point_revision: 0,
intermediate_anchor: *b"_DMI_",
structure_table_address: 0,
structure_table_length: 0,
}
}
}
#[derive(Debug, Clone)]
pub struct SmbiosStructureHeader {
pub struct_type: u8,
pub length: u8,
pub handle: u16,
}
impl SmbiosStructureHeader {
pub fn parse(data: &[u8]) -> Option<Self> {
if data.len() < 4 {
return None;
}
Some(Self {
struct_type: data[0],
length: data[1],
handle: u16::from_le_bytes(data[2..4].try_into().ok()?),
})
}
}
#[derive(Debug, Clone)]
pub struct BiosInterruptCall {
pub vector: u8,
pub function: u8,
pub sub_function: u8,
pub inputs: BiosRegisterInputs,
pub outputs: Vec<String>,
pub description: String,
}
#[derive(Debug, Clone, Default)]
pub struct BiosRegisterInputs {
pub ax: Option<u16>,
pub bx: Option<u16>,
pub cx: Option<u16>,
pub dx: Option<u16>,
pub si: Option<u16>,
pub di: Option<u16>,
pub es: Option<u16>,
pub ds: Option<u16>,
}
impl BiosInterruptCall {
pub fn int10_teletype_output() -> Self {
Self {
vector: 0x10,
function: 0x0E,
sub_function: 0,
inputs: BiosRegisterInputs {
ax: Some(0x0E00),
bx: Some(0x0000), ..Default::default()
},
outputs: vec![],
description: "Write character in AL in teletype mode".into(),
}
}
pub fn int10_set_video_mode(mode: u8) -> Self {
Self {
vector: 0x10,
function: 0x00,
sub_function: 0,
inputs: BiosRegisterInputs {
ax: Some(mode as u16),
..Default::default()
},
outputs: vec![],
description: format!("Set video mode to {:02X}h", mode),
}
}
pub fn int13_read_sectors(drive: u8, head: u8, track: u8, sector: u8, count: u8) -> Self {
let cx =
((track as u16 & 0xFF) << 8) | ((track as u16 & 0x300) >> 2) | (sector as u16 & 0x3F);
Self {
vector: 0x13,
function: 0x02,
sub_function: count,
inputs: BiosRegisterInputs {
ax: Some(0x0200 | count as u16),
cx: Some(cx),
dx: Some(((head as u16) << 8) | drive as u16),
..Default::default()
},
outputs: vec!["AH:return_code".into(), "AL:sectors_read".into()],
description: "Read sectors from disk (CHS)".into(),
}
}
pub fn int13_extended_read(drive: u8) -> Self {
Self {
vector: 0x13,
function: 0x42,
sub_function: 0,
inputs: BiosRegisterInputs {
ax: Some(0x4200),
dx: Some(drive as u16),
si: Some(0), ..Default::default()
},
outputs: vec!["AH:return_code".into()],
description: "Extended read sectors from disk (LBA)".into(),
}
}
pub fn int15_get_extended_memory() -> Self {
Self {
vector: 0x15,
function: 0x88,
sub_function: 0,
inputs: BiosRegisterInputs {
ax: Some(0x8800),
..Default::default()
},
outputs: vec!["AX:extended_memory_kb".into()],
description: "Get extended memory size (above 1 MB) in KB".into(),
}
}
pub fn int15_e820_memory_map() -> Self {
Self {
vector: 0x15,
function: 0xE8,
sub_function: 0x20,
inputs: BiosRegisterInputs {
ax: Some(0xE820),
bx: Some(0), cx: Some(24), dx: Some((0x534D4150u32 & 0xFFFF) as u16), di: Some(0), es: Some(0), ..Default::default()
},
outputs: vec![
"EAX:0x534D4150".into(),
"EBX:continuation".into(),
"ECX:valid_bytes".into(),
],
description: "Get system memory map (E820)".into(),
}
}
pub fn int16_read_keyboard() -> Self {
Self {
vector: 0x16,
function: 0x00,
sub_function: 0,
inputs: BiosRegisterInputs {
ax: Some(0x0000),
..Default::default()
},
outputs: vec!["AH:scan_code".into(), "AL:ascii_char".into()],
description: "Read keyboard character".into(),
}
}
pub fn int16_check_keyboard() -> Self {
Self {
vector: 0x16,
function: 0x01,
sub_function: 0,
inputs: BiosRegisterInputs {
ax: Some(0x0100),
..Default::default()
},
outputs: vec!["ZF:status".into(), "AX:char_if_available".into()],
description: "Check keyboard status (non-blocking)".into(),
}
}
pub fn to_asm(&self) -> String {
let mut asm = String::new();
asm.push_str(&format!(
"; BIOS INT 0x{:02X}, AH=0x{:02X} — {}\n",
self.vector, self.function, self.description
));
if let Some(ax) = self.inputs.ax {
asm.push_str(&format!(" mov ax, 0x{:04X}\n", ax));
}
if let Some(bx) = self.inputs.bx {
asm.push_str(&format!(" mov bx, 0x{:04X}\n", bx));
}
if let Some(cx) = self.inputs.cx {
asm.push_str(&format!(" mov cx, 0x{:04X}\n", cx));
}
if let Some(dx) = self.inputs.dx {
asm.push_str(&format!(" mov dx, 0x{:04X}\n", dx));
}
asm.push_str(&format!(" int 0x{:02X}\n", self.vector));
asm
}
}
#[derive(Debug, Clone)]
pub struct MultibootInfo {
pub flags: u32,
pub mem_lower: u32,
pub mem_upper: u32,
pub boot_device: u32,
pub cmdline: u32,
pub mods_count: u32,
pub mods_addr: u32,
pub elf_sec: MultibootElfSections,
pub mmap_length: u32,
pub mmap_addr: u32,
pub drives_length: u32,
pub drives_addr: u32,
pub config_table: u32,
pub boot_loader_name: u32,
pub apm_table: u32,
pub vbe_control_info: u32,
pub vbe_mode_info: u32,
pub vbe_mode: u16,
pub vbe_interface_seg: u16,
pub vbe_interface_off: u16,
pub vbe_interface_len: u16,
pub framebuffer_addr: u64,
pub framebuffer_pitch: u32,
pub framebuffer_width: u32,
pub framebuffer_height: u32,
pub framebuffer_bpp: u8,
pub framebuffer_type: u8,
}
#[derive(Debug, Clone, Copy)]
pub struct MultibootElfSections {
pub num: u32,
pub size: u32,
pub addr: u32,
pub shndx: u32,
}
impl MultibootInfo {
pub fn new() -> Self {
Self {
flags: 0,
mem_lower: 0,
mem_upper: 0,
boot_device: 0,
cmdline: 0,
mods_count: 0,
mods_addr: 0,
elf_sec: MultibootElfSections {
num: 0,
size: 0,
addr: 0,
shndx: 0,
},
mmap_length: 0,
mmap_addr: 0,
drives_length: 0,
drives_addr: 0,
config_table: 0,
boot_loader_name: 0,
apm_table: 0,
vbe_control_info: 0,
vbe_mode_info: 0,
vbe_mode: 0,
vbe_interface_seg: 0,
vbe_interface_off: 0,
vbe_interface_len: 0,
framebuffer_addr: 0,
framebuffer_pitch: 0,
framebuffer_width: 0,
framebuffer_height: 0,
framebuffer_bpp: 0,
framebuffer_type: 0,
}
}
pub fn has_memory_info(&self) -> bool {
(self.flags & 0x01) != 0
}
pub fn has_boot_device(&self) -> bool {
(self.flags & 0x02) != 0
}
pub fn has_cmdline(&self) -> bool {
(self.flags & 0x04) != 0
}
pub fn has_modules(&self) -> bool {
(self.flags & 0x08) != 0
}
pub fn has_mmap(&self) -> bool {
(self.flags & 0x40) != 0
}
pub fn has_framebuffer(&self) -> bool {
(self.flags & 0x800) != 0
}
pub fn total_memory_bytes(&self) -> u64 {
(self.mem_lower as u64 + self.mem_upper as u64) * 1024
}
}
impl Default for MultibootInfo {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct UefiRuntimeServices {
pub runtime_services: u64,
pub get_time: Option<u64>,
pub set_time: Option<u64>,
pub get_wakeup_time: Option<u64>,
pub set_wakeup_time: Option<u64>,
pub set_virtual_address_map: Option<u64>,
pub convert_pointer: Option<u64>,
pub get_variable: Option<u64>,
pub get_next_variable_name: Option<u64>,
pub set_variable: Option<u64>,
pub get_next_high_monotonic_count: Option<u64>,
pub reset_system: Option<u64>,
pub update_capsule: Option<u64>,
pub query_capsule_capabilities: Option<u64>,
pub query_variable_info: Option<u64>,
}
impl UefiRuntimeServices {
pub fn new(rt_addr: u64) -> Self {
Self {
runtime_services: rt_addr,
get_time: None,
set_time: None,
get_wakeup_time: None,
set_wakeup_time: None,
set_virtual_address_map: None,
convert_pointer: None,
get_variable: None,
get_next_variable_name: None,
set_variable: None,
get_next_high_monotonic_count: None,
reset_system: None,
update_capsule: None,
query_capsule_capabilities: None,
query_variable_info: None,
}
}
pub fn generate_get_time_call(&self) -> String {
r#"/* UEFI GetTime Runtime Service Call */
EFI_STATUS efi_get_time(EFI_TIME *time, EFI_TIME_CAPABILITIES *capabilities) {
if (!gRT || !gRT->GetTime)
return EFI_UNSUPPORTED;
return gRT->GetTime(time, capabilities);
}
"#
.to_string()
}
pub fn generate_reset_system_call(&self) -> String {
r#"/* UEFI ResetSystem Runtime Service Call */
void efi_reset_system(EFI_RESET_TYPE reset_type, EFI_STATUS reset_status,
UINTN data_size, VOID *reset_data) {
if (gRT && gRT->ResetSystem)
gRT->ResetSystem(reset_type, reset_status, data_size, reset_data);
}
"#
.to_string()
}
pub fn generate_get_variable_call(&self) -> String {
r#"/* UEFI GetVariable Runtime Service Call */
EFI_STATUS efi_get_variable(CHAR16 *name, EFI_GUID *guid,
UINT32 *attributes, UINTN *data_size, VOID *data) {
if (!gRT || !gRT->GetVariable)
return EFI_UNSUPPORTED;
return gRT->GetVariable(name, guid, attributes, data_size, data);
}
"#
.to_string()
}
}
impl Default for UefiRuntimeServices {
fn default() -> Self {
Self::new(0)
}
}
#[derive(Debug, Clone)]
pub struct X86FirmwareSupport {
pub acpi_enabled: bool,
pub smbios_enabled: bool,
pub uefi_runtime_enabled: bool,
pub bios_interrupts_enabled: bool,
pub multiboot_info_available: bool,
pub acpi_rsdp_address: Option<u64>,
pub smbios_entry_point: Option<u64>,
pub uefi_system_table: Option<u64>,
pub uefi_runtime: Option<UefiRuntimeServices>,
pub multiboot_info_address: Option<u64>,
pub bios_calls: Vec<BiosInterruptCall>,
pub acpi_tables: Vec<AcpiSdtHeader>,
}
impl X86FirmwareSupport {
pub fn new() -> Self {
Self {
acpi_enabled: true,
smbios_enabled: false,
uefi_runtime_enabled: false,
bios_interrupts_enabled: true,
multiboot_info_available: false,
acpi_rsdp_address: None,
smbios_entry_point: None,
uefi_system_table: None,
uefi_runtime: None,
multiboot_info_address: None,
bios_calls: Vec::new(),
acpi_tables: Vec::new(),
}
}
pub fn for_uefi(system_table: u64) -> Self {
Self {
acpi_enabled: true,
smbios_enabled: true,
uefi_runtime_enabled: true,
bios_interrupts_enabled: false,
multiboot_info_available: false,
acpi_rsdp_address: None,
smbios_entry_point: None,
uefi_system_table: Some(system_table),
uefi_runtime: Some(UefiRuntimeServices::new(system_table)),
multiboot_info_address: None,
bios_calls: Vec::new(),
acpi_tables: Vec::new(),
}
}
pub fn for_bios() -> Self {
let mut fw = Self::new();
fw.bios_interrupts_enabled = true;
fw.bios_calls
.push(BiosInterruptCall::int15_get_extended_memory());
fw.bios_calls
.push(BiosInterruptCall::int15_e820_memory_map());
fw
}
pub fn for_multiboot(mbinfo_addr: u64) -> Self {
Self {
acpi_enabled: false,
smbios_enabled: false,
uefi_runtime_enabled: false,
bios_interrupts_enabled: false,
multiboot_info_available: true,
acpi_rsdp_address: None,
smbios_entry_point: None,
uefi_system_table: None,
uefi_runtime: None,
multiboot_info_address: Some(mbinfo_addr),
bios_calls: Vec::new(),
acpi_tables: Vec::new(),
}
}
pub fn search_rsdp(&self) -> Option<u64> {
self.acpi_rsdp_address
}
pub fn search_smbios(&self) -> Option<u64> {
self.smbios_entry_point
}
pub fn with_bios_call(mut self, call: BiosInterruptCall) -> Self {
self.bios_calls.push(call);
self
}
pub fn with_acpi_rsdp(mut self, addr: u64) -> Self {
self.acpi_rsdp_address = Some(addr);
self
}
pub fn with_smbios(mut self, addr: u64) -> Self {
self.smbios_entry_point = Some(addr);
self
}
pub fn with_acpi(mut self, enabled: bool) -> Self {
self.acpi_enabled = enabled;
self
}
pub fn generate_rsdp_search_c(&self) -> String {
r#"/* ACPI RSDP Detection — generated by llvm-native */
#include <stdint.h>
#define RSDP_SIGNATURE "RSD PTR "
typedef struct {
char signature[8];
uint8_t checksum;
char oem_id[6];
uint8_t revision;
uint32_t rsdt_address;
} __attribute__((packed)) rsdp_t;
typedef struct {
char signature[8];
uint8_t checksum;
char oem_id[6];
uint8_t revision;
uint32_t rsdt_address;
uint32_t length;
uint64_t xsdt_address;
uint8_t extended_checksum;
uint8_t reserved[3];
} __attribute__((packed)) rsdp2_t;
void *find_rsdp(void) {
/* Scan EBDA and BIOS areas for "RSD PTR " */
uint8_t *start = (uint8_t *)0x000E0000;
uint8_t *end = (uint8_t *)0x00100000;
for (uint8_t *p = start; p < end; p += 16) {
if (*(uint64_t *)p == *(uint64_t *)RSDP_SIGNATURE) {
/* Validate checksum */
uint8_t sum = 0;
rsdp_t *rsdp = (rsdp_t *)p;
uint8_t len = (rsdp->revision >= 2) ? 36 : 20;
for (int i = 0; i < len; i++) {
sum += p[i];
}
if (sum == 0) {
return rsdp;
}
}
}
return NULL;
}
"#
.to_string()
}
pub fn generate_multiboot_parser_c(&self) -> String {
r#"/* Multiboot Info Parser — generated by llvm-native */
#include <stdint.h>
#define MULTIBOOT_INFO_MEMORY 0x00000001
#define MULTIBOOT_INFO_BOOTDEV 0x00000002
#define MULTIBOOT_INFO_CMDLINE 0x00000004
#define MULTIBOOT_INFO_MODS 0x00000008
#define MULTIBOOT_INFO_MMAP 0x00000040
#define MULTIBOOT_INFO_FRAMEBUF 0x00000800
typedef struct {
uint32_t flags;
uint32_t mem_lower;
uint32_t mem_upper;
uint32_t boot_device;
uint32_t cmdline;
uint32_t mods_count;
uint32_t mods_addr;
/* ... additional fields as needed ... */
uint32_t mmap_length;
uint32_t mmap_addr;
} __attribute__((packed)) multiboot_info_t;
typedef struct {
uint32_t size;
uint64_t base_addr;
uint64_t length;
uint32_t type;
} __attribute__((packed)) multiboot_mmap_entry_t;
void parse_multiboot_info(multiboot_info_t *mbi) {
if (mbi->flags & MULTIBOOT_INFO_MEMORY) {
/* Memory available: mem_lower + mem_upper KB */
uint64_t total = (uint64_t)(mbi->mem_lower + mbi->mem_upper) * 1024;
(void)total;
}
if (mbi->flags & MULTIBOOT_INFO_MMAP) {
multiboot_mmap_entry_t *mmap = (multiboot_mmap_entry_t *)(uintptr_t)mbi->mmap_addr;
uint8_t *end = (uint8_t *)mmap + mbi->mmap_length;
while ((uint8_t *)mmap < end) {
if (mmap->type == 1) {
/* Available RAM at base_addr, length bytes */
}
mmap = (multiboot_mmap_entry_t *)((uint8_t *)mmap + mmap->size + 4);
}
}
}
"#
.to_string()
}
}
impl Default for X86FirmwareSupport {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bitness_pointer_width() {
assert_eq!(X86Bitness::Bits16.pointer_width(), 2);
assert_eq!(X86Bitness::Bits32.pointer_width(), 4);
assert_eq!(X86Bitness::Bits64.pointer_width(), 8);
}
#[test]
fn test_bitness_march_flag() {
assert_eq!(X86Bitness::Bits16.march_flag(), "-m16");
assert_eq!(X86Bitness::Bits32.march_flag(), "-m32");
assert_eq!(X86Bitness::Bits64.march_flag(), "-m64");
}
#[test]
fn test_bitness_triple_suffix() {
assert_eq!(X86Bitness::Bits32.triple_suffix(), "i386");
assert_eq!(X86Bitness::Bits64.triple_suffix(), "x86_64");
}
#[test]
fn test_bitness_is_long_mode() {
assert!(X86Bitness::Bits64.is_long_mode());
assert!(!X86Bitness::Bits32.is_long_mode());
assert!(!X86Bitness::Bits16.is_long_mode());
}
#[test]
fn test_bitness_display() {
assert_eq!(format!("{}", X86Bitness::Bits16), "16-bit");
assert_eq!(format!("{}", X86Bitness::Bits32), "32-bit");
assert_eq!(format!("{}", X86Bitness::Bits64), "64-bit");
}
#[test]
fn test_freestanding_env_default() {
let env = X86FreestandingEnv::default();
assert!(env.freestanding);
assert!(env.nostdlib);
assert!(env.nostdinc);
assert!(env.nodefaultlibs);
assert_eq!(env.stack_size, 65536);
assert_eq!(env.heap_size, 1048576);
}
#[test]
fn test_freestanding_env_generate_flags() {
let env = X86FreestandingEnv::new();
let flags = env.generate_flags();
assert!(flags.contains(&"-ffreestanding".to_string()));
assert!(flags.contains(&"-nostdlib".to_string()));
assert!(flags.contains(&"-nostdinc".to_string()));
assert!(flags.contains(&"-nodefaultlibs".to_string()));
assert!(flags.iter().any(|f| f.starts_with("-D__STDC_HOSTED__")));
}
#[test]
fn test_freestanding_env_for_kernel() {
let env = X86FreestandingEnv::for_kernel();
let flags = env.generate_flags();
assert!(flags.iter().any(|f| f.contains("__KERNEL__")));
assert!(env.nolibc);
assert!(env.nostartfiles);
}
#[test]
fn test_freestanding_env_for_uefi() {
let env = X86FreestandingEnv::for_uefi();
let flags = env.generate_flags();
assert!(flags.iter().any(|f| f.contains("__UEFI_APPLICATION__")));
assert!(env.nostdlib);
}
#[test]
fn test_freestanding_env_is_fully_freestanding() {
let env = X86FreestandingEnv::new();
assert!(env.is_fully_freestanding());
}
#[test]
fn test_freestanding_env_with_stack_size() {
let env = X86FreestandingEnv::new().with_stack_size(131072);
assert_eq!(env.stack_size, 131072);
}
#[test]
fn test_freestanding_env_with_heap_size() {
let env = X86FreestandingEnv::new().with_heap_size(2097152);
assert_eq!(env.heap_size, 2097152);
}
#[test]
fn test_freestanding_env_target_triple() {
let env = X86FreestandingEnv::default();
assert_eq!(
env.target_triple(X86Bitness::Bits32),
"i386-unknown-none-elf"
);
assert_eq!(
env.target_triple(X86Bitness::Bits64),
"x86_64-unknown-none-elf"
);
}
#[test]
fn test_bare_metal_target_default() {
let t = X86BareMetalTarget::default();
assert_eq!(t.kind, X86BareMetalTargetKind::BareMetal);
assert_eq!(t.bitness, X86Bitness::Bits64);
assert_eq!(t.entry_point, "_start");
assert_eq!(t.output_format, "elf");
}
#[test]
fn test_bare_metal_target_bios_boot() {
let t = X86BareMetalTarget::bios_boot();
assert_eq!(t.kind, X86BareMetalTargetKind::BiosBoot);
assert_eq!(t.bitness, X86Bitness::Bits16);
assert_eq!(t.output_format, "binary");
assert_eq!(t.image_base, 0x7C00);
}
#[test]
fn test_bare_metal_target_uefi_boot_64() {
let t = X86BareMetalTarget::uefi_boot_64();
assert_eq!(t.kind, X86BareMetalTargetKind::UefiBoot);
assert_eq!(t.entry_point, "efi_main");
assert_eq!(t.output_format, "pe");
assert!(t.is_pe_output());
}
#[test]
fn test_bare_metal_target_multiboot() {
let t = X86BareMetalTarget::multiboot();
assert_eq!(t.kind, X86BareMetalTargetKind::Multiboot);
assert_eq!(t.bitness, X86Bitness::Bits32);
assert_eq!(t.output_format, "elf");
assert!(t.is_elf_output());
}
#[test]
fn test_bare_metal_target_multiboot2() {
let t = X86BareMetalTarget::multiboot2();
assert_eq!(t.bitness, X86Bitness::Bits64);
assert_eq!(t.triple, "x86_64-unknown-none-elf");
}
#[test]
fn test_bare_metal_target_coreboot() {
let t = X86BareMetalTarget::coreboot();
assert_eq!(t.kind, X86BareMetalTargetKind::Coreboot);
assert_eq!(t.output_format, "elf");
}
#[test]
fn test_bare_metal_target_linux_kernel_module() {
let t = X86BareMetalTarget::linux_kernel_module();
assert_eq!(t.kind, X86BareMetalTargetKind::LinuxKernelModule);
assert_eq!(t.entry_point, "init_module");
assert_eq!(t.code_model(), "kernel");
}
#[test]
fn test_bare_metal_target_efi_byte_code() {
let t = X86BareMetalTarget::efi_byte_code();
assert_eq!(t.kind, X86BareMetalTargetKind::EfiByteCode);
assert_eq!(t.output_format, "ebc");
}
#[test]
fn test_bare_metal_target_kind_as_str() {
assert_eq!(X86BareMetalTargetKind::BareMetal.as_str(), "bare-metal");
assert_eq!(X86BareMetalTargetKind::BiosBoot.as_str(), "bios-boot");
assert_eq!(X86BareMetalTargetKind::UefiBoot.as_str(), "uefi-boot");
assert_eq!(X86BareMetalTargetKind::Multiboot.as_str(), "multiboot");
assert_eq!(X86BareMetalTargetKind::Coreboot.as_str(), "coreboot");
assert_eq!(
X86BareMetalTargetKind::LinuxKernelModule.as_str(),
"linux-kernel-module"
);
assert_eq!(
X86BareMetalTargetKind::EfiByteCode.as_str(),
"efi-byte-code"
);
}
#[test]
fn test_bare_metal_target_kind_requires_boot_loader() {
assert!(X86BareMetalTargetKind::BiosBoot.requires_boot_loader());
assert!(X86BareMetalTargetKind::UefiBoot.requires_boot_loader());
assert!(!X86BareMetalTargetKind::BareMetal.requires_boot_loader());
assert!(!X86BareMetalTargetKind::LinuxKernelModule.requires_boot_loader());
}
#[test]
fn test_bare_metal_target_generate_flags() {
let t = X86BareMetalTarget::new();
let flags = t.generate_flags();
assert!(flags.iter().any(|f| f.contains("-target")));
assert!(flags.iter().any(|f| f == "-m64"));
}
#[test]
fn test_boot_loader_bios_boot_sector() {
let bl = X86BootLoader::bios_boot_sector();
assert_eq!(bl.kind, BootLoaderKind::BiosBootSector);
assert_eq!(bl.bitness, X86Bitness::Bits16);
let sector = bl.generate_boot_sector();
assert!(sector.is_some());
let sector = sector.unwrap();
assert_eq!(sector.len(), 512);
assert_eq!(sector[510], 0x55);
assert_eq!(sector[511], 0xAA);
assert_eq!(sector[0], 0xEB); }
#[test]
fn test_boot_loader_not_bios() {
let bl = X86BootLoader::uefi_application();
assert_eq!(bl.generate_boot_sector(), None);
}
#[test]
fn test_boot_loader_multiboot_v1() {
let bl = X86BootLoader::multiboot_v1();
assert_eq!(bl.kind, BootLoaderKind::MultibootV1);
assert!(bl.multiboot_header.is_some());
}
#[test]
fn test_boot_loader_multiboot_v2() {
let bl = X86BootLoader::multiboot_v2();
assert_eq!(bl.kind, BootLoaderKind::MultibootV2);
assert!(bl.multiboot2_header.is_some());
}
#[test]
fn test_boot_loader_generate_uefi_entry_c() {
let bl = X86BootLoader::uefi_application();
let c = bl.generate_uefi_entry_c();
assert!(c.contains("efi_main"));
assert!(c.contains("EFI_STATUS"));
assert!(c.contains("kernel_main()"));
}
#[test]
fn test_boot_loader_with_boot_message() {
let bl = X86BootLoader::bios_boot_sector().with_boot_message("Hello World!");
let sector = bl.generate_boot_sector().unwrap();
let msg = "Hello World!";
let found = sector.windows(msg.len()).any(|w| w == msg.as_bytes());
assert!(found);
}
#[test]
fn test_multiboot_header_default() {
let mh = MultibootHeader::new();
assert_eq!(mh.magic, 0x1BADB002);
assert_eq!(mh.magic.wrapping_add(mh.flags).wrapping_add(mh.checksum), 0);
}
#[test]
fn test_multiboot_header_to_bytes() {
let mh = MultibootHeader::new();
let bytes = mh.to_bytes();
assert!(bytes.len() >= 12);
let magic = u32::from_le_bytes(bytes[0..4].try_into().unwrap());
assert_eq!(magic, 0x1BADB002);
}
#[test]
fn test_multiboot2_header_to_bytes() {
let mh2 = Multiboot2Header::new_i386();
let bytes = mh2.to_bytes();
let magic = u32::from_le_bytes(bytes[0..4].try_into().unwrap());
assert_eq!(magic, 0xE85250D6);
}
#[test]
fn test_bios_dap() {
let dap = BiosDap::new(1, 64, 0x1000, 0x0000);
assert_eq!(dap.size, 16);
assert_eq!(dap.sector_count, 64);
assert_eq!(dap.lba_low, 1);
let bytes = dap.to_bytes();
assert_eq!(bytes.len(), 16);
assert_eq!(bytes[0], 16);
assert_eq!(bytes[2], 64);
}
#[test]
fn test_linker_script_default() {
let ls = X86LinkerScript::default();
assert_eq!(ls.arch, "i386:x86-64");
assert_eq!(ls.entry_point, "_start");
assert!(ls.memory_regions.len() > 0);
assert!(ls.sections.len() >= 4);
}
#[test]
fn test_linker_script_32bit() {
let ls = X86LinkerScript::new_32bit();
assert_eq!(ls.bitness, X86Bitness::Bits32);
assert_eq!(ls.arch, "i386");
assert_eq!(ls.output_format, "elf32-i386");
}
#[test]
fn test_linker_script_bios() {
let ls = X86LinkerScript::new_bios();
assert_eq!(ls.bitness, X86Bitness::Bits16);
assert_eq!(ls.output_format, "binary");
assert_eq!(ls.start_address, Some(0x7C00));
}
#[test]
fn test_linker_script_uefi() {
let ls = X86LinkerScript::new_uefi();
assert_eq!(ls.output_format, "pei-x86-64");
assert_eq!(ls.entry_point, "efi_main");
}
#[test]
fn test_linker_script_generate() {
let ls = X86LinkerScript::default();
let ld = ls.generate();
assert!(ld.contains("OUTPUT_FORMAT"));
assert!(ld.contains("ENTRY(_start)"));
assert!(ld.contains("MEMORY"));
assert!(ld.contains(".text"));
assert!(ld.contains(".data"));
assert!(ld.contains(".bss"));
assert!(ld.contains(".rodata"));
assert!(ld.contains("__bss_start"));
assert!(ld.contains("__stack_top"));
assert!(ld.contains("__heap_start"));
}
#[test]
fn test_linker_script_with_stack_heap() {
let ls = X86LinkerScript::new()
.with_stack_size(131072)
.with_heap_size(2097152);
assert_eq!(ls.stack_size, 131072);
assert_eq!(ls.heap_size, 2097152);
let ld = ls.generate();
assert!(ld.contains("0x20000"));
assert!(ld.contains("0x200000"));
}
#[test]
fn test_memory_region() {
let region = X86MemoryRegion::ram_64();
assert_eq!(region.name, "RAM64");
assert_eq!(region.attributes, "rwx");
assert!(region.length > 0);
}
#[test]
fn test_section_placement() {
let sp = X86SectionPlacement::new(".text", "RAM")
.with_alignment(0x1000)
.with_keep(true);
assert_eq!(sp.name, ".text");
assert_eq!(sp.alignment, 0x1000);
assert!(sp.keep);
}
#[test]
fn test_intrinsics_lookup_cli() {
let intrin = X86IntrinsicsEmbedded::new();
let cli = intrin.lookup("__builtin_ia32_cli");
assert!(cli.is_some());
assert_eq!(cli.unwrap(), EmbeddedX86Intrinsic::Cli);
}
#[test]
fn test_intrinsics_lookup_sti() {
let intrin = X86IntrinsicsEmbedded::new();
let sti = intrin.lookup("__builtin_ia32_sti");
assert!(sti.is_some());
assert_eq!(sti.unwrap(), EmbeddedX86Intrinsic::Sti);
}
#[test]
fn test_intrinsics_lookup_inb() {
let intrin = X86IntrinsicsEmbedded::new();
let inb = intrin.lookup("__builtin_ia32_inb");
assert!(inb.is_some());
assert_eq!(inb.unwrap(), EmbeddedX86Intrinsic::Inb);
}
#[test]
fn test_intrinsics_lookup_rdmsr() {
let intrin = X86IntrinsicsEmbedded::new();
let rdmsr = intrin.lookup("__builtin_ia32_rdmsr");
assert!(rdmsr.is_some());
assert_eq!(rdmsr.unwrap(), EmbeddedX86Intrinsic::Rdmsr);
}
#[test]
fn test_intrinsics_lookup_cpuid() {
let intrin = X86IntrinsicsEmbedded::new();
let cpuid = intrin.lookup("__builtin_ia32_cpuid");
assert!(cpuid.is_some());
assert_eq!(cpuid.unwrap(), EmbeddedX86Intrinsic::Cpuid);
}
#[test]
fn test_intrinsics_lookup_mfence() {
let intrin = X86IntrinsicsEmbedded::new();
let mfence = intrin.lookup("__builtin_ia32_mfence");
assert!(mfence.is_some());
assert_eq!(mfence.unwrap(), EmbeddedX86Intrinsic::Mfence);
}
#[test]
fn test_intrinsics_count() {
let intrin = X86IntrinsicsEmbedded::new();
assert_eq!(intrin.count(), EmbeddedX86Intrinsic::COUNT);
assert!(!intrin.is_empty());
}
#[test]
fn test_intrinsics_requires_ring0() {
assert!(EmbeddedX86Intrinsic::Cli.requires_ring0());
assert!(EmbeddedX86Intrinsic::Lgdt.requires_ring0());
assert!(EmbeddedX86Intrinsic::Rdmsr.requires_ring0());
assert!(!EmbeddedX86Intrinsic::Cpuid.requires_ring0());
assert!(!EmbeddedX86Intrinsic::Rdtsc.requires_ring0());
}
#[test]
fn test_intrinsics_requires_64bit() {
assert!(EmbeddedX86Intrinsic::Swapgs.requires_64bit());
assert!(!EmbeddedX86Intrinsic::Cli.requires_64bit());
}
#[test]
fn test_intrinsics_can_use_ring0_restricted() {
let intrin = X86IntrinsicsEmbedded::new().with_ring0(false);
assert!(!intrin.can_use(EmbeddedX86Intrinsic::Cli));
assert!(intrin.can_use(EmbeddedX86Intrinsic::Cpuid));
assert!(intrin.can_use(EmbeddedX86Intrinsic::Rdtsc));
}
#[test]
fn test_intrinsics_can_use_64bit_restricted() {
let intrin = X86IntrinsicsEmbedded::new().with_64bit(false);
assert!(!intrin.can_use(EmbeddedX86Intrinsic::Swapgs));
assert!(intrin.can_use(EmbeddedX86Intrinsic::Cli));
}
#[test]
fn test_intrinsics_mnemonic() {
assert_eq!(EmbeddedX86Intrinsic::Cli.mnemonic(), "cli");
assert_eq!(EmbeddedX86Intrinsic::Hlt.mnemonic(), "hlt");
assert_eq!(EmbeddedX86Intrinsic::Cpuid.mnemonic(), "cpuid");
assert_eq!(EmbeddedX86Intrinsic::Mfence.mnemonic(), "mfence");
}
#[test]
fn test_intrinsics_builtin_name() {
assert_eq!(
EmbeddedX86Intrinsic::Cli.builtin_name(),
"__builtin_ia32_cli"
);
assert_eq!(
EmbeddedX86Intrinsic::Lidt.builtin_name(),
"__builtin_ia32_lidt"
);
}
#[test]
fn test_intrinsics_operand_count() {
assert_eq!(EmbeddedX86Intrinsic::Cli.operand_count(), 0);
assert_eq!(EmbeddedX86Intrinsic::Inb.operand_count(), 2);
assert_eq!(EmbeddedX86Intrinsic::Cpuid.operand_count(), 4);
assert_eq!(EmbeddedX86Intrinsic::Xsetbv.operand_count(), 2);
}
#[test]
fn test_intrinsics_category() {
assert_eq!(
EmbeddedX86Intrinsic::Cli.category(),
EmbeddedIntrinsicCategory::Interrupt
);
assert_eq!(
EmbeddedX86Intrinsic::Inb.category(),
EmbeddedIntrinsicCategory::IoPort
);
assert_eq!(
EmbeddedX86Intrinsic::Mfence.category(),
EmbeddedIntrinsicCategory::Fence
);
}
#[test]
fn test_intrinsics_generate_inline_asm() {
let asm = EmbeddedX86Intrinsic::Cli.generate_inline_asm(true);
assert_eq!(asm, "cli");
let asm = EmbeddedX86Intrinsic::Mfence.generate_inline_asm(false);
assert_eq!(asm, "mfence");
}
#[test]
fn test_intrinsics_generate_header() {
let intrin = X86IntrinsicsEmbedded::new();
let header = intrin.generate_header();
assert!(header.contains("__X86_EMBEDDED_INTRINSICS_H"));
assert!(header.contains("__builtin_ia32_cli"));
assert!(header.contains("__builtin_ia32_rdmsr"));
assert!(header.contains("__builtin_ia32_inb"));
}
#[test]
fn test_interrupt_handlers_new() {
let ih = X86InterruptHandlers::new();
assert!(ih.idt_enabled);
assert_eq!(ih.entries.len(), 256);
assert_eq!(ih.code_selector, 0x08);
assert_eq!(ih.handler_count(), 0);
}
#[test]
fn test_interrupt_handlers_register_exception() {
let mut ih = X86InterruptHandlers::new();
ih.register_exception(ExceptionVector::PageFault, 0x1000);
assert!(ih.has_handler(14));
assert_eq!(ih.handler_count(), 1);
}
#[test]
fn test_interrupt_handlers_register_irq() {
let mut ih = X86InterruptHandlers::new();
ih.register_irq(32, 0x2000);
assert!(ih.has_handler(32));
assert_eq!(ih.handler_count(), 1);
}
#[test]
fn test_idt_entry_new_64() {
let entry = IdtEntry::new_64(0x12345678, 0x08, InterruptGateType::InterruptGate64, 0, 0);
assert_eq!(entry.offset_low, 0x5678);
assert_eq!(entry.offset_mid, 0x1234);
assert_eq!(entry.selector, 0x08);
assert!(entry.present);
}
#[test]
fn test_idt_entry_encode_64() {
let entry = IdtEntry::new_64(0xDEADBEEF, 0x08, InterruptGateType::InterruptGate64, 0, 0);
let enc = entry.encode_64();
assert_eq!(enc.len(), 16);
}
#[test]
fn test_idt_entry_encode_32() {
let entry = IdtEntry::new_32(0xDEADBEEF, 0x08, InterruptGateType::InterruptGate32, 0);
let enc = entry.encode_32();
assert_eq!(enc.len(), 8);
}
#[test]
fn test_idt_descriptor() {
let desc = IdtDescriptor::new(0x1000, 256);
assert_eq!(desc.limit, 256 * 16 - 1);
assert_eq!(desc.base, 0x1000);
}
#[test]
fn test_exception_vector_has_error_code() {
assert!(!ExceptionVector::DivideError.has_error_code());
assert!(ExceptionVector::PageFault.has_error_code());
assert!(ExceptionVector::GeneralProtectionFault.has_error_code());
assert!(!ExceptionVector::Debug.has_error_code());
}
#[test]
fn test_exception_vector_name() {
assert_eq!(ExceptionVector::PageFault.name(), "#PF");
assert_eq!(ExceptionVector::DivideError.name(), "#DE");
assert_eq!(ExceptionVector::GeneralProtectionFault.name(), "#GP");
}
#[test]
fn test_generate_isr_wrapper() {
let ih = X86InterruptHandlers::new();
let asm = ih.generate_isr_wrapper(14, true);
assert!(asm.contains("isr14:"));
assert!(asm.contains("iretq"));
assert!(asm.contains("call isr14_handler"));
}
#[test]
fn test_generate_page_fault_handler_c() {
let ih = X86InterruptHandlers::new();
let c = ih.generate_page_fault_handler_c();
assert!(c.contains("isr14_handler"));
assert!(c.contains("cr2"));
assert!(c.contains("page fault"));
}
#[test]
fn test_generate_syscall_setup_asm() {
let mut ih = X86InterruptHandlers::new();
ih.syscall_msr_star = Some(0x0010000800000000);
ih.syscall_msr_lstar = Some(0xFFFF800000001000);
let asm = ih.generate_syscall_setup_asm();
assert!(asm.contains("syscall_init"));
assert!(asm.contains("wrmsr"));
}
#[test]
fn test_with_syscall_handler() {
let ih = X86InterruptHandlers::new().with_syscall_handler(
0x1000,
0x0010000800000000,
0xFFFF800000001000,
);
assert_eq!(ih.syscall_handler, Some(0x1000));
assert_eq!(ih.syscall_msr_star, Some(0x0010000800000000));
}
#[test]
fn test_paging_support_default() {
let ps = X86PagingSupport::default();
assert!(ps.enabled);
assert_eq!(ps.bitness, X86Bitness::Bits64);
assert_eq!(ps.paging_level, PagingLevel::Level4);
assert_eq!(ps.identity_map_bytes, 0x200000);
}
#[test]
fn test_page_table_entry() {
let pte = PageTableEntry::new(0x1000, PageFlags::kernel_rw());
assert_eq!(pte.physical_address(), 0x1000);
assert!(pte.is_present());
assert!(!pte.is_huge());
}
#[test]
fn test_page_table_entry_empty() {
let pte = PageTableEntry::empty();
assert!(!pte.is_present());
assert_eq!(pte.physical_address(), 0);
}
#[test]
fn test_page_flags_kernel_rw() {
let flags = PageFlags::kernel_rw();
assert!(flags.present);
assert!(flags.writable);
assert!(!flags.user_accessible);
let bits = flags.bits();
assert_eq!(bits & PTE_PRESENT, 0); assert_ne!(bits & PTE_WRITABLE, 0);
assert_eq!(bits & PTE_USER, 0);
}
#[test]
fn test_page_flags_user_rw() {
let flags = PageFlags::user_rw();
assert!(flags.user_accessible);
let bits = flags.bits();
assert_ne!(bits & PTE_USER, 0);
}
#[test]
fn test_page_flags_kernel_rx() {
let flags = PageFlags::kernel_rx();
assert!(!flags.writable);
let bits = flags.bits();
assert_eq!(bits & PTE_WRITABLE, 0);
}
#[test]
fn test_page_flags_kernel_rw_nx() {
let flags = PageFlags::kernel_rw_nx();
assert!(flags.no_execute);
let bits = flags.bits();
assert_ne!(bits & PTE_NX, 0);
}
#[test]
fn test_page_table_level_index() {
let vaddr: u64 = 0xFFFF_8000_0010_0000;
assert_eq!(PageTableLevel::Pml4.index_of(vaddr), 0x1FF);
assert_eq!(PageTableLevel::Pdpt.index_of(vaddr), 0x100);
assert_eq!(PageTableLevel::Pd.index_of(vaddr), 0x000);
assert_eq!(PageTableLevel::Pt.index_of(vaddr), 0x100);
}
#[test]
fn test_paging_level_va_bits() {
assert_eq!(PagingLevel::Level4.va_bits(), 48);
assert_eq!(PagingLevel::Level5.va_bits(), 57);
}
#[test]
fn test_paging_generate_page_tables_asm() {
let ps = X86PagingSupport::new();
let asm = ps.generate_page_tables_asm();
assert!(asm.is_some());
let asm = asm.unwrap();
assert!(asm.contains("pml4:"));
assert!(asm.contains("enable_paging:"));
assert!(asm.contains("movq %rax, %cr3"));
}
#[test]
fn test_paging_32bit_no_asm() {
let ps = X86PagingSupport::new_32bit_pae();
assert!(ps.generate_page_tables_asm().is_none());
}
#[test]
fn test_firmware_support_default() {
let fw = X86FirmwareSupport::default();
assert!(fw.acpi_enabled);
assert!(!fw.smbios_enabled);
assert!(fw.bios_interrupts_enabled);
assert!(!fw.uefi_runtime_enabled);
}
#[test]
fn test_firmware_support_for_uefi() {
let fw = X86FirmwareSupport::for_uefi(0x1000);
assert!(fw.uefi_runtime_enabled);
assert!(!fw.bios_interrupts_enabled);
assert!(fw.uefi_runtime.is_some());
}
#[test]
fn test_firmware_support_for_bios() {
let fw = X86FirmwareSupport::for_bios();
assert!(fw.bios_interrupts_enabled);
assert!(!fw.bios_calls.is_empty());
}
#[test]
fn test_firmware_support_for_multiboot() {
let fw = X86FirmwareSupport::for_multiboot(0x90000);
assert!(fw.multiboot_info_available);
assert_eq!(fw.multiboot_info_address, Some(0x90000));
}
#[test]
fn test_acpi_rsdp_default() {
let rsdp = AcpiRsdp::default();
assert_eq!(&rsdp.signature, b"RSD PTR ");
assert_eq!(rsdp.revision, 2);
}
#[test]
fn test_acpi_sdt_header_parse() {
let data: [u8; 36] = [
b'F', b'A', b'C', b'P', 0xF4, 0x01, 0x00, 0x00, 0x06, 0x00, b'L', b'L', b'V', b'M', b'N', b'A', 0x00, 0x00, b'M', b'Y', b'P', b'C', 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, b'L', b'L', b'V', b'M', 0x01, 0x00, 0x00, 0x00, ];
let hdr = AcpiSdtHeader::parse(&data);
assert!(hdr.is_some());
let hdr = hdr.unwrap();
assert_eq!(hdr.signature_str(), "FACP");
assert_eq!(hdr.length, 500);
assert_eq!(hdr.revision, 6);
}
#[test]
fn test_acpi_table_kind_from_signature() {
assert_eq!(AcpiTableKind::from_signature(b"FACP"), AcpiTableKind::Fadt);
assert_eq!(AcpiTableKind::from_signature(b"APIC"), AcpiTableKind::Madt);
assert_eq!(AcpiTableKind::from_signature(b"MCFG"), AcpiTableKind::Mcfg);
assert_eq!(AcpiTableKind::from_signature(b"DSDT"), AcpiTableKind::Dsdt);
}
#[test]
fn test_smbios_entry_point() {
let ep = SmbiosEntryPoint::new_v3();
assert!(ep.validate());
assert_eq!(ep.major_version, 3);
assert_eq!(ep.anchor_str(), "_SM3_");
}
#[test]
fn test_smbios_structure_header_parse() {
let data: [u8; 4] = [0x00, 0x1A, 0x00, 0x10]; let hdr = SmbiosStructureHeader::parse(&data);
assert!(hdr.is_some());
let hdr = hdr.unwrap();
assert_eq!(hdr.struct_type, 0);
assert_eq!(hdr.length, 0x1A);
assert_eq!(hdr.handle, 0x1000);
}
#[test]
fn test_bios_interrupt_call_int10_teletype() {
let call = BiosInterruptCall::int10_teletype_output();
assert_eq!(call.vector, 0x10);
assert_eq!(call.function, 0x0E);
let asm = call.to_asm();
assert!(asm.contains("int 0x10"));
}
#[test]
fn test_bios_interrupt_call_int13_read() {
let call = BiosInterruptCall::int13_read_sectors(0x80, 0, 0, 2, 3);
assert_eq!(call.vector, 0x13);
assert_eq!(call.function, 0x02);
let asm = call.to_asm();
assert!(asm.contains("int 0x13"));
}
#[test]
fn test_bios_interrupt_call_int15_e820() {
let call = BiosInterruptCall::int15_e820_memory_map();
assert_eq!(call.vector, 0x15);
assert_eq!(call.function, 0xE8);
let asm = call.to_asm();
assert!(asm.contains("int 0x15"));
}
#[test]
fn test_bios_interrupt_call_int16_keyboard() {
let call = BiosInterruptCall::int16_read_keyboard();
assert_eq!(call.vector, 0x16);
assert_eq!(call.function, 0x00);
}
#[test]
fn test_multiboot_info_has_flags() {
let mut mbi = MultibootInfo::new();
assert!(!mbi.has_memory_info());
mbi.flags = 0x01;
assert!(mbi.has_memory_info());
mbi.flags = 0x40;
assert!(mbi.has_mmap());
mbi.flags = 0x800;
assert!(mbi.has_framebuffer());
}
#[test]
fn test_multiboot_info_total_memory() {
let mut mbi = MultibootInfo::new();
mbi.mem_lower = 640;
mbi.mem_upper = 32768;
assert_eq!(mbi.total_memory_bytes(), (640 + 32768) * 1024);
}
#[test]
fn test_uefi_runtime_services_new() {
let rt = UefiRuntimeServices::new(0xDEAD);
assert_eq!(rt.runtime_services, 0xDEAD);
}
#[test]
fn test_uefi_runtime_services_generate_get_time() {
let rt = UefiRuntimeServices::new(0);
let code = rt.generate_get_time_call();
assert!(code.contains("GetTime"));
assert!(code.contains("EFI_STATUS"));
}
#[test]
fn test_uefi_runtime_services_generate_reset_system() {
let rt = UefiRuntimeServices::new(0);
let code = rt.generate_reset_system_call();
assert!(code.contains("ResetSystem"));
}
#[test]
fn test_firmware_generate_rsdp_search_c() {
let fw = X86FirmwareSupport::default();
let c = fw.generate_rsdp_search_c();
assert!(c.contains("RSD PTR"));
assert!(c.contains("find_rsdp"));
assert!(c.contains("rsdp_t"));
}
#[test]
fn test_firmware_generate_multiboot_parser_c() {
let fw = X86FirmwareSupport::for_multiboot(0x90000);
let c = fw.generate_multiboot_parser_c();
assert!(c.contains("multiboot_info_t"));
assert!(c.contains("parse_multiboot_info"));
}
#[test]
fn test_x86_embedded_new() {
let emb = X86Embedded::new();
assert_eq!(emb.opt_level, "2");
assert!(!emb.debug_info);
assert!(emb.freestanding.freestanding);
}
#[test]
fn test_x86_embedded_new_32bit() {
let emb = X86Embedded::new_32bit();
assert_eq!(emb.target.bitness, X86Bitness::Bits32);
assert!(emb.compiler_flags.contains(&"-m32".to_string()));
}
#[test]
fn test_x86_embedded_new_64bit() {
let emb = X86Embedded::new_64bit();
assert_eq!(emb.target.bitness, X86Bitness::Bits64);
assert!(emb.compiler_flags.contains(&"-m64".to_string()));
}
#[test]
fn test_x86_embedded_generate_compiler_flags() {
let emb = X86Embedded::new();
let flags = emb.generate_compiler_flags();
assert!(flags.iter().any(|f| f == "-ffreestanding"));
assert!(flags.iter().any(|f| f == "-O2"));
}
#[test]
fn test_x86_embedded_generate_linker_flags() {
let emb = X86Embedded::new();
let flags = emb.generate_linker_flags();
assert!(flags.iter().any(|f| f.contains("-T")));
}
#[test]
fn test_x86_embedded_generate_all() {
let emb = X86Embedded::new();
let artifacts = emb.generate_all();
assert!(!artifacts.compiler_flags.is_empty());
assert!(!artifacts.linker_flags.is_empty());
assert!(!artifacts.linker_script.is_empty());
assert!(!artifacts.startup_asm.is_empty());
assert!(!artifacts.crt0_source.is_empty());
}
#[test]
fn test_x86_embedded_generate_startup_asm_64() {
let emb = X86Embedded::new_64bit();
let asm = emb.generate_startup_asm();
assert!(asm.contains("_start:"));
assert!(asm.contains("kernel_main"));
}
#[test]
fn test_x86_embedded_generate_startup_asm_32() {
let emb = X86Embedded::new_32bit();
let asm = emb.generate_startup_asm();
assert!(asm.contains("_start:"));
assert!(asm.contains("pushl"));
}
#[test]
fn test_x86_embedded_generate_crt0() {
let emb = X86Embedded::new();
let crt0 = emb.generate_crt0();
assert!(crt0.contains("zero_bss"));
assert!(crt0.contains("init_data"));
assert!(crt0.contains("__bss_start"));
}
#[test]
fn test_x86_embedded_with_opt_level() {
let emb = X86Embedded::new().with_opt_level("s");
assert_eq!(emb.opt_level, "s");
}
#[test]
fn test_x86_embedded_with_debug_info() {
let emb = X86Embedded::new().with_debug_info(true);
assert!(emb.debug_info);
let flags = emb.generate_compiler_flags();
assert!(flags.contains(&"-g".to_string()));
}
#[test]
fn test_x86_embedded_add_flag() {
let emb = X86Embedded::new().add_flag("-mno-red-zone");
assert!(emb.compiler_flags.contains(&"-mno-red-zone".to_string()));
}
#[test]
fn test_x86_embedded_add_linker_flag() {
let emb = X86Embedded::new().add_linker_flag("-z max-page-size=0x1000");
assert!(emb.linker_flags.iter().any(|f| f.contains("max-page-size")));
}
#[test]
fn test_x86_embedded_default_trait() {
let emb: X86Embedded = Default::default();
assert_eq!(emb.opt_level, "2");
}
#[test]
fn test_full_bios_boot_workflow() {
let mut emb = X86Embedded::new();
emb.target = X86BareMetalTarget::bios_boot();
emb.boot_loader = X86BootLoader::bios_boot_sector().with_boot_message("BOOT");
emb.linker_script = X86LinkerScript::new_bios();
let artifacts = emb.generate_all();
assert!(artifacts.boot_sector.is_some());
assert!(!artifacts.linker_script.is_empty());
assert!(!artifacts.startup_asm.is_empty());
}
#[test]
fn test_full_uefi_workflow() {
let mut emb = X86Embedded::new_64bit();
emb.target = X86BareMetalTarget::uefi_boot_64();
emb.boot_loader = X86BootLoader::uefi_application();
emb.linker_script = X86LinkerScript::new_uefi();
emb.freestanding = X86FreestandingEnv::for_uefi();
emb.firmware = X86FirmwareSupport::for_uefi(0);
let flags = emb.generate_compiler_flags();
assert!(flags.iter().any(|f| f.contains("__UEFI_APPLICATION__")));
let entry = emb.boot_loader.generate_uefi_entry_c();
assert!(entry.contains("efi_main"));
}
#[test]
fn test_full_multiboot_workflow() {
let mut emb = X86Embedded::new_32bit();
emb.target = X86BareMetalTarget::multiboot();
emb.boot_loader = X86BootLoader::multiboot_v1();
emb.linker_script = X86LinkerScript::new_multiboot();
emb.firmware = X86FirmwareSupport::for_multiboot(0x90000);
let artifacts = emb.generate_all();
assert!(!artifacts.linker_script.is_empty());
let ld = emb.linker_script.generate();
assert!(ld.contains(".multiboot"));
}
#[test]
fn test_linux_kernel_module_workflow() {
let mut emb = X86Embedded::new_64bit();
emb.target = X86BareMetalTarget::linux_kernel_module();
emb.freestanding = X86FreestandingEnv::for_kernel();
let flags = emb.generate_compiler_flags();
assert!(flags.iter().any(|f| f.contains("-mcmodel=kernel")));
}
#[test]
fn test_idt_all_exceptions_registered() {
let mut ih = X86InterruptHandlers::new();
let exceptions = [
ExceptionVector::DivideError,
ExceptionVector::Debug,
ExceptionVector::Nmi,
ExceptionVector::Breakpoint,
ExceptionVector::Overflow,
ExceptionVector::InvalidOpcode,
ExceptionVector::DoubleFault,
ExceptionVector::InvalidTss,
ExceptionVector::SegmentNotPresent,
ExceptionVector::StackSegmentFault,
ExceptionVector::GeneralProtectionFault,
ExceptionVector::PageFault,
];
for ex in &exceptions {
ih.register_exception(*ex, 0x1000 + ex.number() as u64 * 0x10);
}
assert_eq!(ih.handler_count(), exceptions.len());
for ex in &exceptions {
assert!(ih.has_handler(ex.number()));
}
}
#[test]
fn test_paging_full_generation() {
let ps = X86PagingSupport::new()
.with_identity_map(0x400000)
.with_kernel_vbase(0xFFFF_8000_0000_0000)
.with_kernel_pload(0x100000);
let asm = ps.generate_page_tables_asm().unwrap();
assert!(asm.contains("pml4"));
let c = ps.generate_page_tables_c();
assert!(c.contains("init_paging"));
}
#[test]
fn test_all_bios_interrupt_calls() {
let calls = vec![
BiosInterruptCall::int10_teletype_output(),
BiosInterruptCall::int10_set_video_mode(0x03),
BiosInterruptCall::int13_read_sectors(0x80, 0, 0, 1, 1),
BiosInterruptCall::int13_extended_read(0x80),
BiosInterruptCall::int15_get_extended_memory(),
BiosInterruptCall::int15_e820_memory_map(),
BiosInterruptCall::int16_read_keyboard(),
BiosInterruptCall::int16_check_keyboard(),
];
for call in &calls {
let asm = call.to_asm();
assert!(!asm.is_empty());
assert!(asm.contains("int "));
}
}
#[test]
fn test_linker_script_all_formats() {
let formats = [
("default", X86LinkerScript::default()),
("32bit", X86LinkerScript::new_32bit()),
("bios", X86LinkerScript::new_bios()),
("uefi", X86LinkerScript::new_uefi()),
("multiboot", X86LinkerScript::new_multiboot()),
("multiboot2", X86LinkerScript::new_multiboot2()),
];
for (name, ls) in &formats {
let ld = ls.generate();
assert!(!ld.is_empty(), "linker script '{}' is empty", name);
assert!(
ld.contains("ENTRY"),
"linker script '{}' missing ENTRY",
name
);
}
}
#[test]
fn test_embedded_artifact_roundtrip() {
let emb = X86Embedded::new();
let artifacts = emb.generate_all();
assert!(!artifacts.compiler_flags.is_empty());
assert!(!artifacts.linker_flags.is_empty());
assert!(!artifacts.linker_script.is_empty());
assert!(!artifacts.startup_asm.is_empty());
assert!(!artifacts.crt0_source.is_empty());
}
#[test]
fn test_intrinsics_all_have_names() {
let all = [
EmbeddedX86Intrinsic::Cli,
EmbeddedX86Intrinsic::Sti,
EmbeddedX86Intrinsic::Hlt,
EmbeddedX86Intrinsic::Pause,
EmbeddedX86Intrinsic::Lidt,
EmbeddedX86Intrinsic::Sidt,
EmbeddedX86Intrinsic::Lgdt,
EmbeddedX86Intrinsic::Sgdt,
EmbeddedX86Intrinsic::Lldt,
EmbeddedX86Intrinsic::Sldt,
EmbeddedX86Intrinsic::Ltr,
EmbeddedX86Intrinsic::Str,
EmbeddedX86Intrinsic::Inb,
EmbeddedX86Intrinsic::Outb,
EmbeddedX86Intrinsic::Inw,
EmbeddedX86Intrinsic::Outw,
EmbeddedX86Intrinsic::Inl,
EmbeddedX86Intrinsic::Outl,
EmbeddedX86Intrinsic::Rdmsr,
EmbeddedX86Intrinsic::Wrmsr,
EmbeddedX86Intrinsic::Rdtsc,
EmbeddedX86Intrinsic::Rdtscp,
EmbeddedX86Intrinsic::Cpuid,
EmbeddedX86Intrinsic::Invlpg,
EmbeddedX86Intrinsic::Wbinvd,
EmbeddedX86Intrinsic::Invd,
EmbeddedX86Intrinsic::Mfence,
EmbeddedX86Intrinsic::Lfence,
EmbeddedX86Intrinsic::Sfence,
EmbeddedX86Intrinsic::Xsetbv,
EmbeddedX86Intrinsic::Xgetbv,
EmbeddedX86Intrinsic::Rdfsbase,
EmbeddedX86Intrinsic::Rdgsbase,
EmbeddedX86Intrinsic::Wrfsbase,
EmbeddedX86Intrinsic::Wrgsbase,
EmbeddedX86Intrinsic::Swapgs,
EmbeddedX86Intrinsic::Serialize,
EmbeddedX86Intrinsic::Rdpmc,
EmbeddedX86Intrinsic::Rdpid,
EmbeddedX86Intrinsic::Clflush,
EmbeddedX86Intrinsic::Clflushopt,
EmbeddedX86Intrinsic::Clwb,
EmbeddedX86Intrinsic::Prefetch,
EmbeddedX86Intrinsic::Prefetchw,
EmbeddedX86Intrinsic::Monitor,
EmbeddedX86Intrinsic::Monitorx,
EmbeddedX86Intrinsic::Mwait,
EmbeddedX86Intrinsic::Mwaitx,
];
for intrin in &all {
assert!(
!intrin.builtin_name().is_empty(),
"Missing name for {:?}",
intrin
);
assert!(
!intrin.mnemonic().is_empty(),
"Missing mnemonic for {:?}",
intrin
);
assert!(
intrin.builtin_name().starts_with("__builtin_ia32_"),
"Name '{}' does not start with __builtin_ia32_",
intrin.builtin_name()
);
}
}
#[test]
fn test_intrinsics_index_roundtrip() {
for i in 0..EmbeddedX86Intrinsic::COUNT {
let intrin = match i {
0 => EmbeddedX86Intrinsic::Cli,
1 => EmbeddedX86Intrinsic::Sti,
2 => EmbeddedX86Intrinsic::Hlt,
12 => EmbeddedX86Intrinsic::Inb,
18 => EmbeddedX86Intrinsic::Rdmsr,
22 => EmbeddedX86Intrinsic::Cpuid,
26 => EmbeddedX86Intrinsic::Mfence,
35 => EmbeddedX86Intrinsic::Swapgs,
_ => continue,
};
assert_eq!(intrin.index(), i);
}
}
#[test]
fn test_bare_metal_target_all_kinds() {
let kinds = [
X86BareMetalTargetKind::BareMetal,
X86BareMetalTargetKind::BiosBoot,
X86BareMetalTargetKind::UefiBoot,
X86BareMetalTargetKind::Multiboot,
X86BareMetalTargetKind::Coreboot,
X86BareMetalTargetKind::LinuxKernelModule,
X86BareMetalTargetKind::EfiByteCode,
X86BareMetalTargetKind::Custom,
];
for kind in &kinds {
assert!(!kind.as_str().is_empty());
assert!(!kind.default_output_format().is_empty());
assert!(!kind.entry_point().is_empty());
}
}
#[test]
fn test_boot_loader_all_kinds() {
let kinds = [
BootLoaderKind::BiosBootSector,
BootLoaderKind::UefiApplication,
BootLoaderKind::MultibootV1,
BootLoaderKind::MultibootV2,
BootLoaderKind::LinuxBzImage,
BootLoaderKind::CorebootPayload,
BootLoaderKind::Custom,
];
for kind in &kinds {
assert!(!kind.as_str().is_empty());
let bl = X86BootLoader::new(*kind);
assert_eq!(bl.kind, *kind);
}
}
#[test]
fn test_memory_range_types() {
let types = [
MemoryRangeType::Available,
MemoryRangeType::Reserved,
MemoryRangeType::AcpiReclaim,
MemoryRangeType::AcpiNvs,
MemoryRangeType::BadMemory,
MemoryRangeType::KernelCode,
MemoryRangeType::KernelData,
MemoryRangeType::KernelStack,
MemoryRangeType::Mmio,
];
for ty in &types {
let range = PhysicalMemoryRange {
base_address: 0,
length: 0x1000,
range_type: *ty,
};
assert_eq!(range.range_type, *ty);
}
}
#[test]
fn test_page_table_constants() {
assert_eq!(PTE_PRESENT, 1);
assert_eq!(PTE_WRITABLE, 2);
assert_eq!(PTE_USER, 4);
assert_eq!(PTE_HUGE, 128);
assert_eq!(PTE_NX, 1u64 << 63);
}
#[test]
fn test_freestanding_no_libc() {
let env = X86FreestandingEnv::new();
assert!(env.is_no_libc());
}
#[test]
fn test_page_table_entry_set_address() {
let mut pte = PageTableEntry::new(0x1000, PageFlags::kernel_rw());
pte.set_address(0x2000);
assert_eq!(pte.physical_address(), 0x2000);
}
#[test]
fn test_page_table_entry_set_flags() {
let mut pte = PageTableEntry::new(0x1000, PageFlags::kernel_rw());
pte.set_flags(PageFlags::user_rw());
assert!((pte.raw & PTE_USER) != 0);
}
#[test]
fn test_x86_embedded_full_build_pipeline() {
let emb = X86Embedded::new_64bit()
.with_opt_level("3")
.with_debug_info(true)
.add_flag("-mno-red-zone")
.add_flag("-mno-mmx")
.add_flag("-mno-sse");
let artifacts = emb.generate_all();
assert!(artifacts.compiler_flags.contains(&"-O3".to_string()));
assert!(artifacts.compiler_flags.contains(&"-g".to_string()));
assert!(artifacts
.compiler_flags
.contains(&"-mno-red-zone".to_string()));
assert!(artifacts.startup_asm.contains("kernel_main"));
assert!(artifacts.linker_script.contains("ENTRY(_start)"));
}
#[test]
fn test_all_idt_vector_slots() {
let mut ih = X86InterruptHandlers::new();
for vec in 0..256u8 {
assert!(!ih.has_handler(vec));
}
ih.register_irq(32, 0x4000);
assert!(ih.has_handler(32));
assert!(!ih.has_handler(33));
}
#[test]
fn test_firmware_bios_calls_count() {
let fw = X86FirmwareSupport::for_bios();
assert!(fw.bios_calls.len() >= 2);
}
#[test]
fn test_idt_descriptor_encode_32() {
let desc = IdtDescriptor::new(0x8000, 256);
let enc = desc.encode_32();
assert_eq!(enc.len(), 6);
assert_eq!(u16::from_le_bytes([enc[0], enc[1]]), 256 * 16 - 1);
}
#[test]
fn test_idt_descriptor_encode_64() {
let desc = IdtDescriptor::new(0x10000, 128);
let enc = desc.encode_64();
assert_eq!(enc.len(), 10);
}
#[test]
fn test_paging_virtual_to_physical() {
let ps = X86PagingSupport::new();
let pages = vec![PageTableEntry::new(0x1000, PageFlags::kernel_rw())];
let phys = ps.virtual_to_physical(&pages, 0);
assert_eq!(phys, Some(0x1000));
}
#[test]
fn test_paging_virtual_to_physical_not_present() {
let ps = X86PagingSupport::new();
let pages = vec![PageTableEntry::empty()];
let phys = ps.virtual_to_physical(&pages, 0);
assert_eq!(phys, None);
}
#[test]
fn test_embedded_all_intrinsics_found() {
let intrin = X86IntrinsicsEmbedded::new();
let names = [
"__builtin_ia32_cli",
"__builtin_ia32_sti",
"__builtin_ia32_hlt",
"__builtin_ia32_pause",
"__builtin_ia32_lidt",
"__builtin_ia32_sidt",
"__builtin_ia32_lgdt",
"__builtin_ia32_sgdt",
"__builtin_ia32_lldt",
"__builtin_ia32_sldt",
"__builtin_ia32_ltr",
"__builtin_ia32_str",
"__builtin_ia32_inb",
"__builtin_ia32_outb",
"__builtin_ia32_inw",
"__builtin_ia32_outw",
"__builtin_ia32_inl",
"__builtin_ia32_outl",
"__builtin_ia32_rdmsr",
"__builtin_ia32_wrmsr",
"__builtin_ia32_rdtsc",
"__builtin_ia32_rdtscp",
"__builtin_ia32_cpuid",
"__builtin_ia32_invlpg",
"__builtin_ia32_wbinvd",
"__builtin_ia32_mfence",
"__builtin_ia32_lfence",
"__builtin_ia32_sfence",
"__builtin_ia32_xsetbv",
"__builtin_ia32_xgetbv",
"__builtin_ia32_swapgs",
"__builtin_ia32_serialize",
];
for name in &names {
assert!(
intrin.lookup(name).is_some(),
"Intrinsic {} not found",
name
);
}
}
#[test]
fn test_multiboot_header_new_full() {
let mh = MultibootHeader::new_full(0x100000, 0x100000, 0x200000, 0x300000, 1024, 768, 32);
assert_eq!(mh.magic, 0x1BADB002);
assert!(mh.flags & 0x00000004 != 0); let bytes = mh.to_bytes();
assert!(bytes.len() >= 28);
}
#[test]
fn test_bzimage_setup_header_size() {
let bl = X86BootLoader::linux_bzimage();
let setup = bl.generate_bzimage_setup();
assert_eq!(setup.len(), 512);
let hdr_off = 0x1F1;
assert_eq!(setup[hdr_off + 18], b'H');
assert_eq!(setup[hdr_off + 19], b'd');
assert_eq!(setup[hdr_off + 20], b'r');
assert_eq!(setup[hdr_off + 21], b'S');
}
#[test]
fn test_x86_embedded_uefi_full_integration() {
let emb = X86Embedded::new()
.add_flag("-target x86_64-unknown-windows-msvc")
.add_flag("-ffreestanding");
let artifacts = emb.generate_all();
assert!(!artifacts.startup_asm.is_empty());
assert!(!artifacts.crt0_source.is_empty());
assert!(artifacts.crt0_source.contains("__bss_start"));
assert!(artifacts.crt0_source.contains("zero_bss"));
}
#[test]
fn test_interrupt_handler_ist_setup() {
let mut ih = X86InterruptHandlers::new();
ih.register_with_ist(8, 0x2000, 1, 0); assert!(ih.has_handler(8));
let entry = ih.entries[8].as_ref().unwrap();
assert_eq!(entry.ist, 1);
assert_eq!(entry.dpl, 0);
}
#[test]
fn test_interrupt_handler_user_dpl() {
let mut ih = X86InterruptHandlers::new();
ih.register_with_ist(0x80, 0x3000, 0, 3); assert!(ih.has_handler(0x80));
let entry = ih.entries[0x80].as_ref().unwrap();
assert_eq!(entry.dpl, 3);
}
#[test]
fn test_firmware_search_rsdp_configured() {
let fw = X86FirmwareSupport::default().with_acpi_rsdp(0xE0000);
assert_eq!(fw.search_rsdp(), Some(0xE0000));
}
#[test]
fn test_firmware_search_smbios_configured() {
let fw = X86FirmwareSupport::default().with_smbios(0xF0000);
assert_eq!(fw.search_smbios(), Some(0xF0000));
}
#[test]
fn test_page_table_level_page_size() {
assert_eq!(PageTableLevel::Pt.page_size(), 4096);
assert_eq!(PageTableLevel::Pd.page_size(), 2 * 1024 * 1024);
assert_eq!(PageTableLevel::Pdpt.page_size(), 1024 * 1024 * 1024);
assert_eq!(
PageTableLevel::Pml4.page_size(),
512u64 * 1024 * 1024 * 1024
);
}
#[test]
fn test_stress_many_memory_regions() {
let mut ls = X86LinkerScript::new();
for i in 0..10 {
ls = ls.add_memory_region(X86MemoryRegion::new(
&format!("REGION{}", i),
i * 0x1000000,
0x100000,
"rwx",
));
}
assert_eq!(ls.region_count(), 11); }
#[test]
fn test_stress_many_sections() {
let mut ls = X86LinkerScript::new();
for i in 0..50 {
ls = ls.add_section(X86SectionPlacement::new(&format!(".section{}", i), "RAM64"));
}
assert!(ls.section_count() >= 54); }
#[test]
fn test_stress_many_interrupt_handlers() {
let mut ih = X86InterruptHandlers::new();
for vec in 0..256u8 {
if vec < 32 || vec >= 48 {
ih.register_irq(vec, 0x1000 + vec as u64 * 16);
}
}
assert!(ih.handler_count() > 200);
}
#[test]
fn test_stress_many_compiler_flags() {
let mut emb = X86Embedded::new();
for i in 0..100 {
emb = emb.add_flag(&format!("-Wno-flag-{}", i));
}
let flags = emb.generate_compiler_flags();
assert!(flags.len() >= 100);
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct CpuidResult {
pub eax: u32,
pub ebx: u32,
pub ecx: u32,
pub edx: u32,
}
impl CpuidResult {
pub fn new(eax: u32, ebx: u32, ecx: u32, edx: u32) -> Self {
Self { eax, ebx, ecx, edx }
}
pub fn vendor_string(&self) -> String {
let mut vendor = [0u8; 12];
vendor[0..4].copy_from_slice(&self.ebx.to_le_bytes());
vendor[4..8].copy_from_slice(&self.edx.to_le_bytes());
vendor[8..12].copy_from_slice(&self.ecx.to_le_bytes());
String::from_utf8_lossy(&vendor).to_string()
}
pub fn brand_string(parts: &[CpuidResult; 3]) -> String {
let mut brand = [0u8; 48];
for (i, part) in parts.iter().enumerate() {
let off = i * 16;
brand[off..off + 4].copy_from_slice(&part.eax.to_le_bytes());
brand[off + 4..off + 8].copy_from_slice(&part.ebx.to_le_bytes());
brand[off + 8..off + 12].copy_from_slice(&part.ecx.to_le_bytes());
brand[off + 12..off + 16].copy_from_slice(&part.edx.to_le_bytes());
}
String::from_utf8_lossy(&brand)
.trim_end_matches('\0')
.to_string()
}
}
#[derive(Debug, Clone, Default)]
pub struct CpuidFeatures {
pub sse: bool,
pub sse2: bool,
pub sse3: bool,
pub ssse3: bool,
pub sse41: bool,
pub sse42: bool,
pub avx: bool,
pub avx2: bool,
pub avx512f: bool,
pub avx512dq: bool,
pub avx512bw: bool,
pub avx512vl: bool,
pub avx512vbmi: bool,
pub avx512vbmi2: bool,
pub avx512ifma: bool,
pub avx512bf16: bool,
pub avx512fp16: bool,
pub fma: bool,
pub aes: bool,
pub sha: bool,
pub pclmulqdq: bool,
pub movbe: bool,
pub rdrnd: bool,
pub rdseed: bool,
pub adx: bool,
pub bmi1: bool,
pub bmi2: bool,
pub lzcnt: bool,
pub popcnt: bool,
pub f16c: bool,
pub rdpid: bool,
pub fsgsbase: bool,
pub sgx: bool,
pub clflushopt: bool,
pub clwb: bool,
pub pku: bool,
pub ospke: bool,
pub waitpkg: bool,
pub cet_ibt: bool,
pub cet_ss: bool,
pub serialize: bool,
pub tsx: bool,
pub monitorx: bool,
pub mwaitx: bool,
pub clzero: bool,
pub xsave: bool,
pub osxsave: bool,
pub xsaveopt: bool,
pub xsavec: bool,
pub xsaves: bool,
pub htt: bool,
pub acpi: bool,
pub ss: bool,
pub tm: bool,
pub pbe: bool,
pub dtes64: bool,
pub ds_cpl: bool,
pub vmx: bool,
pub smx: bool,
pub est: bool,
pub tm2: bool,
pub cx16: bool,
pub xtpr: bool,
pub pdcm: bool,
pub pcid: bool,
pub dca: bool,
pub sse4a: bool,
pub xop: bool,
pub fma4: bool,
pub tbm: bool,
pub svm: bool,
pub npt: bool,
pub lbrv: bool,
pub svm_lock: bool,
pub nrip_save: bool,
pub tsc_scale: bool,
pub vmcb_clean: bool,
pub flushbyasid: bool,
pub decode_assists: bool,
pub pausefilter: bool,
pub pfthreshold: bool,
pub avic: bool,
pub v_vmsave_vmload: bool,
pub vgif: bool,
pub gmET: bool,
pub x2avic: bool,
pub sev: bool,
pub sev_es: bool,
pub sev_snp: bool,
pub pge: bool,
pub pat: bool,
pub pse: bool,
pub pse36: bool,
pub mtrr: bool,
pub mca: bool,
pub mce: bool,
pub de: bool,
pub tsc: bool,
pub msr: bool,
pub pae: bool,
pub apic: bool,
pub sep: bool,
pub cmov: bool,
pub clflush: bool,
pub mmx: bool,
pub fxsr: bool,
pub nx: bool,
pub page1gb: bool,
pub lahf_sahf: bool,
pub syscall_sysret: bool,
pub long_mode: bool,
pub smep: bool,
pub smap: bool,
pub umip: bool,
pub pks: bool,
pub x2apic: bool,
}
impl CpuidFeatures {
pub fn decode_standard(results: &[(u32, CpuidResult)]) -> Self {
let mut f = Self::default();
let get = |leaf: u32| -> Option<&CpuidResult> {
results.iter().find(|(l, _)| *l == leaf).map(|(_, r)| r)
};
if let Some(r) = get(1) {
f.sse = (r.edx >> 25) & 1 != 0;
f.sse2 = (r.edx >> 26) & 1 != 0;
f.sse3 = (r.ecx >> 0) & 1 != 0;
f.ssse3 = (r.ecx >> 9) & 1 != 0;
f.sse41 = (r.ecx >> 19) & 1 != 0;
f.sse42 = (r.ecx >> 20) & 1 != 0;
f.avx = (r.ecx >> 28) & 1 != 0;
f.fma = (r.ecx >> 12) & 1 != 0;
f.aes = (r.ecx >> 25) & 1 != 0;
f.pclmulqdq = (r.ecx >> 1) & 1 != 0;
f.rdrnd = (r.ecx >> 30) & 1 != 0;
f.f16c = (r.ecx >> 29) & 1 != 0;
f.osxsave = (r.ecx >> 27) & 1 != 0;
f.mmx = (r.edx >> 23) & 1 != 0;
f.fxsr = (r.edx >> 24) & 1 != 0;
f.pat = (r.edx >> 16) & 1 != 0;
f.pse = (r.edx >> 3) & 1 != 0;
f.pse36 = (r.edx >> 17) & 1 != 0;
f.mtrr = (r.edx >> 12) & 1 != 0;
f.mca = (r.edx >> 14) & 1 != 0;
f.mce = (r.edx >> 7) & 1 != 0;
f.de = (r.edx >> 2) & 1 != 0;
f.tsc = (r.edx >> 4) & 1 != 0;
f.msr = (r.edx >> 5) & 1 != 0;
f.pae = (r.edx >> 6) & 1 != 0;
f.apic = (r.edx >> 9) & 1 != 0;
f.sep = (r.edx >> 11) & 1 != 0;
f.cmov = (r.edx >> 15) & 1 != 0;
f.clflush = (r.edx >> 19) & 1 != 0;
f.htt = (r.edx >> 28) & 1 != 0;
f.ss = (r.edx >> 27) & 1 != 0;
f.tm = (r.edx >> 29) & 1 != 0;
f.pbe = (r.edx >> 31) & 1 != 0;
f.dtes64 = (r.ecx >> 2) & 1 != 0;
f.ds_cpl = (r.ecx >> 4) & 1 != 0;
f.vmx = (r.ecx >> 5) & 1 != 0;
f.smx = (r.ecx >> 6) & 1 != 0;
f.est = (r.ecx >> 7) & 1 != 0;
f.tm2 = (r.ecx >> 8) & 1 != 0;
f.cx16 = (r.ecx >> 13) & 1 != 0;
f.xtpr = (r.ecx >> 14) & 1 != 0;
f.pdcm = (r.ecx >> 15) & 1 != 0;
f.pcid = (r.ecx >> 17) & 1 != 0;
f.dca = (r.ecx >> 18) & 1 != 0;
f.x2apic = (r.ecx >> 21) & 1 != 0;
}
if let Some(r) = get(7) {
f.avx2 = (r.ebx >> 5) & 1 != 0;
f.bmi1 = (r.ebx >> 3) & 1 != 0;
f.bmi2 = (r.ebx >> 8) & 1 != 0;
f.adx = (r.ebx >> 19) & 1 != 0;
f.sha = (r.ebx >> 29) & 1 != 0;
f.avx512f = (r.ebx >> 16) & 1 != 0;
f.avx512dq = (r.ebx >> 17) & 1 != 0;
f.avx512bw = (r.ebx >> 30) & 1 != 0;
f.avx512vl = (r.ebx >> 31) & 1 != 0;
f.avx512ifma = (r.ebx >> 21) & 1 != 0;
f.avx512vbmi = (r.ecx >> 1) & 1 != 0;
f.avx512vbmi2 = (r.ecx >> 6) & 1 != 0;
f.rdseed = (r.ebx >> 18) & 1 != 0;
f.clflushopt = (r.ebx >> 23) & 1 != 0;
f.clwb = (r.ebx >> 24) & 1 != 0;
f.pku = (r.ecx >> 3) & 1 != 0;
f.ospke = (r.ecx >> 4) & 1 != 0;
f.waitpkg = (r.ecx >> 5) & 1 != 0;
f.cet_ibt = (r.edx >> 20) & 1 != 0;
f.cet_ss = (r.ecx >> 7) & 1 != 0;
f.serialize = (r.edx >> 14) & 1 != 0;
f.tsx = (r.ebx >> 11) & 1 != 0;
f.rdpid = (r.ecx >> 22) & 1 != 0;
f.avx512bf16 = (r.eax >> 5) & 1 != 0;
f.avx512fp16 = (r.edx >> 23) & 1 != 0;
f.smep = (r.ebx >> 7) & 1 != 0;
f.smap = (r.ebx >> 20) & 1 != 0;
f.umip = (r.ecx >> 2) & 1 != 0;
f.pks = (r.ecx >> 31) & 1 != 0;
}
if let Some(r) = get(0x80000001) {
f.syscall_sysret = (r.edx >> 11) & 1 != 0;
f.nx = (r.edx >> 20) & 1 != 0;
f.page1gb = (r.edx >> 26) & 1 != 0;
f.lahf_sahf = (r.ecx >> 0) & 1 != 0;
f.long_mode = (r.edx >> 29) & 1 != 0;
f.sse4a = (r.ecx >> 6) & 1 != 0;
f.lzcnt = (r.ecx >> 5) & 1 != 0;
f.popcnt = (r.ecx >> 23) & 1 != 0;
f.xop = (r.ecx >> 11) & 1 != 0;
f.fma4 = (r.ecx >> 16) & 1 != 0;
f.tbm = (r.ecx >> 21) & 1 != 0;
f.svm = (r.ecx >> 2) & 1 != 0;
}
if let Some(r) = get(0x8000000A) {
f.npt = (r.edx >> 0) & 1 != 0;
f.lbrv = (r.edx >> 1) & 1 != 0;
f.svm_lock = (r.edx >> 2) & 1 != 0;
f.nrip_save = (r.edx >> 3) & 1 != 0;
f.tsc_scale = (r.edx >> 4) & 1 != 0;
f.vmcb_clean = (r.edx >> 5) & 1 != 0;
f.flushbyasid = (r.edx >> 6) & 1 != 0;
f.decode_assists = (r.edx >> 7) & 1 != 0;
f.pausefilter = (r.edx >> 10) & 1 != 0;
f.pfthreshold = (r.edx >> 12) & 1 != 0;
f.avic = (r.edx >> 13) & 1 != 0;
f.v_vmsave_vmload = (r.edx >> 15) & 1 != 0;
f.vgif = (r.edx >> 16) & 1 != 0;
f.gmET = (r.edx >> 17) & 1 != 0;
f.x2avic = (r.edx >> 18) & 1 != 0;
}
if let Some(r) = get(0x8000001F) {
f.sev = (r.eax >> 1) & 1 != 0;
f.sev_es = (r.eax >> 3) & 1 != 0;
f.sev_snp = (r.eax >> 4) & 1 != 0;
}
f
}
pub fn new() -> Self {
Self::default()
}
pub fn has_avx512(&self) -> bool {
self.avx512f
}
pub fn has_avx2(&self) -> bool {
self.avx2
}
pub fn is_x86_64_capable(&self) -> bool {
self.long_mode
}
pub fn has_hardware_virtualization(&self) -> bool {
self.vmx || self.svm
}
pub fn enabled_features(&self) -> Vec<String> {
let mut feats = Vec::new();
if self.sse {
feats.push("sse".into());
}
if self.sse2 {
feats.push("sse2".into());
}
if self.sse3 {
feats.push("sse3".into());
}
if self.ssse3 {
feats.push("ssse3".into());
}
if self.sse41 {
feats.push("sse4.1".into());
}
if self.sse42 {
feats.push("sse4.2".into());
}
if self.avx {
feats.push("avx".into());
}
if self.avx2 {
feats.push("avx2".into());
}
if self.avx512f {
feats.push("avx512f".into());
}
if self.aes {
feats.push("aes".into());
}
if self.sha {
feats.push("sha".into());
}
if self.fma {
feats.push("fma".into());
}
if self.bmi1 {
feats.push("bmi1".into());
}
if self.bmi2 {
feats.push("bmi2".into());
}
if self.lzcnt {
feats.push("lzcnt".into());
}
if self.popcnt {
feats.push("popcnt".into());
}
feats
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86Msr {
Ia32McgCap = 0x0179,
Ia32McgStatus = 0x017A,
Ia32McgCtl = 0x017B,
Ia32McgExtCtl = 0x01D0,
Ia32PerfEvtSel0 = 0x0186,
Ia32PerfEvtSel1 = 0x0187,
Ia32PerfEvtSel2 = 0x0188,
Ia32PerfEvtSel3 = 0x0189,
Ia32PerfCtr0 = 0x00C1,
Ia32PerfCtr1 = 0x00C2,
Ia32PerfCtr2 = 0x00C3,
Ia32PerfCtr3 = 0x00C4,
Ia32FixedCtr0 = 0x0309,
Ia32FixedCtr1 = 0x030A,
Ia32FixedCtr2 = 0x030B,
Ia32FixedCtrCtl = 0x038D,
Ia32PerfGlobalStatus = 0x038E,
Ia32PerfGlobalCtl = 0x038F,
Ia32PerfGlobalOvfCtl = 0x0390,
Ia32TimestampCounter = 0x0010,
Ia32ApicBase = 0x001B,
Ia32X2ApicApicId = 0x0802,
Ia32X2ApicVersion = 0x0803,
Ia32X2ApicTpr = 0x0808,
Ia32X2ApicEoi = 0x080B,
Ia32X2ApicLdr = 0x080D,
Ia32X2ApicSpurious = 0x080F,
Ia32X2ApicIsr = 0x0810,
Ia32X2ApicTmr = 0x0818,
Ia32X2ApicIrr = 0x0820,
Ia32X2ApicEsr = 0x0828,
Ia32X2ApicLvtCmci = 0x082F,
Ia32X2ApicIcr = 0x0830,
Ia32X2ApicLvtTimer = 0x0832,
Ia32X2ApicLvtThermal = 0x0833,
Ia32X2ApicLvtPmi = 0x0834,
Ia32X2ApicLvtLint0 = 0x0835,
Ia32X2ApicLvtLint1 = 0x0836,
Ia32X2ApicLvtError = 0x0837,
Ia32X2ApicInitCount = 0x0838,
Ia32X2ApicCurCount = 0x0839,
Ia32X2ApicDivConfig = 0x083E,
Ia32MtrrCap = 0x00FE,
Ia32MtrrDefType = 0x02FF,
Ia32MtrrPhysBase0 = 0x0200,
Ia32MtrrPhysMask0 = 0x0201,
Ia32Star = 0xC000_0081,
Ia32Lstar = 0xC000_0082,
Ia32Cstar = 0xC000_0083,
Ia32Fmask = 0xC000_0084,
Ia32KernelGsBase = 0xC000_0102,
Ia32Efer = 0xC000_0080,
Ia32FsBase = 0xC000_0100,
Ia32GsBase = 0xC000_0101,
Ia32DebugCtl = 0x01D9,
Ia32LastBranchFromIp = 0x01DB,
Ia32LastBranchToIp = 0x01DC,
Ia32LastExceptionFromIp = 0x01DD,
Ia32LastExceptionToIp = 0x01DE,
Ia32VmcsRevisionId = 0x0480,
Ia32FeatureControl = 0x003A,
Ia32PlatformId = 0x0017,
Ia32MiscEnable = 0x01A0,
Ia32EnergyPerfBias = 0x01B0,
Ia32PackageThermStatus = 0x01B1,
Ia32PackageThermInterrupt = 0x01B2,
Ia32PkgThermStatus = 0x01B3,
Ia32TemperatureTarget = 0x01A2,
Ia32TurboRatioLimit = 0x01AD,
Ia32Pmc0 = 0x04C1,
Ia32ArchCapabilities = 0x010A,
Ia32SpecCtrl = 0x0048,
Ia32PredCmd = 0x0049,
Ia32FlushCmd = 0x010B,
Ia32McuOptCtrl = 0x0123,
}
impl X86Msr {
pub fn address(&self) -> u32 {
*self as u32
}
pub fn name(&self) -> &'static str {
match self {
Self::Ia32Star => "IA32_STAR",
Self::Ia32Lstar => "IA32_LSTAR",
Self::Ia32Cstar => "IA32_CSTAR",
Self::Ia32Fmask => "IA32_FMASK",
Self::Ia32Efer => "IA32_EFER",
Self::Ia32FsBase => "IA32_FS_BASE",
Self::Ia32GsBase => "IA32_GS_BASE",
Self::Ia32KernelGsBase => "IA32_KERNEL_GS_BASE",
Self::Ia32ApicBase => "IA32_APIC_BASE",
Self::Ia32MtrrCap => "IA32_MTRR_CAP",
Self::Ia32MtrrDefType => "IA32_MTRR_DEF_TYPE",
Self::Ia32MiscEnable => "IA32_MISC_ENABLE",
Self::Ia32FeatureControl => "IA32_FEATURE_CONTROL",
Self::Ia32SpecCtrl => "IA32_SPEC_CTRL",
Self::Ia32PredCmd => "IA32_PRED_CMD",
Self::Ia32ArchCapabilities => "IA32_ARCH_CAPABILITIES",
Self::Ia32FlushCmd => "IA32_FLUSH_CMD",
Self::Ia32TimestampCounter => "IA32_TIME_STAMP_COUNTER",
Self::Ia32PerfCtr0 => "IA32_PERFCTR0",
Self::Ia32PerfEvtSel0 => "IA32_PERFEVTSEL0",
Self::Ia32PerfGlobalCtl => "IA32_PERF_GLOBAL_CTRL",
Self::Ia32PerfGlobalStatus => "IA32_PERF_GLOBAL_STATUS",
Self::Ia32PerfGlobalOvfCtl => "IA32_PERF_GLOBAL_OVF_CTRL",
Self::Ia32FixedCtrCtl => "IA32_FIXED_CTR_CTRL",
Self::Ia32DebugCtl => "IA32_DEBUGCTL",
Self::Ia32EnergyPerfBias => "IA32_ENERGY_PERF_BIAS",
Self::Ia32TemperatureTarget => "IA32_TEMPERATURE_TARGET",
Self::Ia32McuOptCtrl => "IA32_MCU_OPT_CTRL",
Self::Ia32PkgThermStatus => "IA32_PACKAGE_THERM_STATUS",
_ => "UNKNOWN_MSR",
}
}
pub fn is_user_readable(&self) -> bool {
matches!(
self,
Self::Ia32TimestampCounter | Self::Ia32FsBase | Self::Ia32GsBase | Self::Ia32ApicBase
)
}
pub fn is_user_writable(&self) -> bool {
matches!(self, Self::Ia32FsBase | Self::Ia32GsBase)
}
}
#[derive(Debug, Clone)]
pub struct MsrAccessor {
pub is_64bit: bool,
pub allow_ring0: bool,
}
impl MsrAccessor {
pub fn new(is_64bit: bool, allow_ring0: bool) -> Self {
Self {
is_64bit,
allow_ring0,
}
}
pub fn generate_read_asm(&self, msr: X86Msr) -> String {
format!("movl ${}, %ecx\n rdmsr\n", msr.address())
}
pub fn generate_write_asm(&self, msr: X86Msr) -> String {
format!("movl ${}, %ecx\n wrmsr\n", msr.address())
}
pub fn generate_read_c(&self, msr: X86Msr) -> String {
format!(
"static inline uint64_t rdmsr_{}(void) {{\n uint32_t lo, hi;\n __asm__ volatile(\"rdmsr\" : \"=a\"(lo), \"=d\"(hi) : \"c\"({}));\n return ((uint64_t)hi << 32) | lo;\n}}\n",
msr.name().to_lowercase(),
msr.address()
)
}
pub fn generate_write_c(&self, msr: X86Msr) -> String {
format!(
"static inline void wrmsr_{}(uint64_t val) {{\n uint32_t lo = val, hi = val >> 32;\n __asm__ volatile(\"wrmsr\" :: \"a\"(lo), \"d\"(hi), \"c\"({}));\n}}\n",
msr.name().to_lowercase(),
msr.address()
)
}
}
impl Default for MsrAccessor {
fn default() -> Self {
Self::new(true, true)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IoPort {
PicMasterCommand = 0x20,
PicMasterData = 0x21,
PicSlaveCommand = 0xA0,
PicSlaveData = 0xA1,
PitChannel0 = 0x40,
PitChannel1 = 0x41,
PitChannel2 = 0x42,
PitCommand = 0x43,
KeyboardData = 0x60,
KeyboardStatus = 0x64,
KeyboardCommand = 0x8064,
CmosAddress = 0x70,
CmosData = 0x71,
DmaMasterStatus = 0x08,
DmaMasterCommand = 0x8008,
DmaMasterMask = 0x0A,
DmaSlaveMask = 0xD4,
Com1Base = 0x3F8,
Com2Base = 0x2F8,
Com3Base = 0x3E8,
Com4Base = 0x2E8,
Lpt1Base = 0x378,
Lpt2Base = 0x278,
Lpt3Base = 0x3BC,
VgaCrtcIndex = 0x3D4,
VgaCrtcData = 0x3D5,
VgaInputStatus = 0x3DA,
VgaAttributeIndex = 0x3C0,
VgaAttributeData = 0x3C1,
VgaMiscOutput = 0x3C2,
VgaSequencerIndex = 0x3C4,
VgaSequencerData = 0x3C5,
VgaGraphicsIndex = 0x3CE,
VgaGraphicsData = 0x3CF,
VgaDacIndexRead = 0x3C7,
VgaDacIndexWrite = 0x3C8,
VgaDacData = 0x3C9,
A20FastGate = 0x92,
PciConfigAddress = 0xCF8,
PciConfigData = 0xCFC,
IdePrimaryData = 0x1F0,
IdePrimaryError = 0x1F1,
IdePrimarySectorCount = 0x1F2,
IdePrimaryLbaLow = 0x1F3,
IdePrimaryLbaMid = 0x1F4,
IdePrimaryLbaHigh = 0x1F5,
IdePrimaryDrive = 0x1F6,
IdePrimaryStatus = 0x1F7,
IdePrimaryCommand = 0x81F7,
IdeSecondaryData = 0x170,
IdeSecondaryError = 0x171,
IdeSecondarySectorCount = 0x172,
IdeSecondaryLbaLow = 0x173,
IdeSecondaryLbaMid = 0x174,
IdeSecondaryLbaHigh = 0x175,
IdeSecondaryDrive = 0x176,
IdeSecondaryStatus = 0x177,
IdeSecondaryCommand = 0x8177,
}
impl IoPort {
pub fn address(&self) -> u16 {
*self as u16
}
pub fn name(&self) -> &'static str {
match self {
Self::PicMasterCommand => "PIC Master Command",
Self::PicMasterData => "PIC Master Data",
Self::PicSlaveCommand => "PIC Slave Command",
Self::PicSlaveData => "PIC Slave Data",
Self::PitChannel0 => "PIT Channel 0",
Self::PitChannel1 => "PIT Channel 1",
Self::PitChannel2 => "PIT Channel 2",
Self::PitCommand => "PIT Command",
Self::KeyboardData => "Keyboard Data",
Self::KeyboardStatus => "Keyboard Status/Command",
Self::CmosAddress => "CMOS Address",
Self::CmosData => "CMOS Data",
Self::Com1Base => "COM1 Base",
Self::Com2Base => "COM2 Base",
Self::Lpt1Base => "LPT1 Base",
Self::VgaCrtcIndex => "VGA CRTC Index",
Self::VgaCrtcData => "VGA CRTC Data",
Self::PciConfigAddress => "PCI Config Address",
Self::PciConfigData => "PCI Config Data",
Self::IdePrimaryCommand => "IDE Primary Command",
Self::A20FastGate => "A20 Fast Gate",
_ => "Unknown I/O Port",
}
}
pub fn generate_inb(&self) -> String {
format!("movw ${}, %dx\n inb %dx, %al\n", self.address())
}
pub fn generate_outb(&self) -> String {
format!("movw ${}, %dx\n outb %al, %dx\n", self.address())
}
pub fn generate_read_c(&self) -> String {
format!(
"static inline uint8_t inb_{}(void) {{ uint8_t v; __asm__ volatile(\"inb %1, %0\" : \"=a\"(v) : \"Nd\"({})); return v; }}\n",
self.name().to_lowercase().replace(' ', "_").replace('/', "_"),
self.address()
)
}
pub fn generate_write_c(&self) -> String {
format!(
"static inline void outb_{}(uint8_t v) {{ __asm__ volatile(\"outb %0, %1\" :: \"a\"(v), \"Nd\"({})); }}\n",
self.name().to_lowercase().replace(' ', "_").replace('/', "_"),
self.address()
)
}
}
#[derive(Debug, Clone)]
pub struct PicController {
pub master_offset: u8,
pub slave_offset: u8,
pub initialized: bool,
pub master_mask: u8,
pub slave_mask: u8,
}
impl PicController {
pub fn new() -> Self {
Self {
master_offset: 0x20,
slave_offset: 0x28,
initialized: false,
master_mask: 0xFF,
slave_mask: 0xFF,
}
}
pub fn generate_remap_asm(&self) -> String {
let mut asm = String::new();
asm.push_str("/* 8259 PIC Remapping — generated by llvm-native */\n");
asm.push_str(&format!(
" ; Remap master PIC to 0x{:02X}\n",
self.master_offset
));
asm.push_str(&format!(
" ; Remap slave PIC to 0x{:02X}\n",
self.slave_offset
));
asm.push_str(" ; ICW1: start initialization sequence (cascade mode)\n");
asm.push_str(" mov al, 0x11\n");
asm.push_str(" out 0x20, al\n");
asm.push_str(" out 0xA0, al\n");
asm.push_str(&format!(
" ; ICW2: master vector offset = 0x{:02X}\n",
self.master_offset
));
asm.push_str(&format!(" mov al, 0x{:02X}\n", self.master_offset));
asm.push_str(" out 0x21, al\n");
asm.push_str(&format!(
" ; ICW2: slave vector offset = 0x{:02X}\n",
self.slave_offset
));
asm.push_str(&format!(" mov al, 0x{:02X}\n", self.slave_offset));
asm.push_str(" out 0xA1, al\n");
asm.push_str(" ; ICW3: master-slave connection\n");
asm.push_str(" mov al, 0x04 ; slave connected to IRQ2\n");
asm.push_str(" out 0x21, al\n");
asm.push_str(" mov al, 0x02 ; slave ID = 2\n");
asm.push_str(" out 0xA1, al\n");
asm.push_str(" ; ICW4: 8086 mode + auto EOI\n");
asm.push_str(" mov al, 0x01\n");
asm.push_str(" out 0x21, al\n");
asm.push_str(" out 0xA1, al\n");
asm.push_str(" ; Mask all interrupts initially\n");
asm.push_str(" mov al, 0xFF\n");
asm.push_str(" out 0x21, al\n");
asm.push_str(" out 0xA1, al\n");
asm
}
pub fn generate_remap_c(&self) -> String {
format!(
r#"/* 8259 PIC Remapping — generated by llvm-native */
static inline void pic_remap(void) {{
/* ICW1: Initialize in cascade mode */
outb(0x20, 0x11);
outb(0xA0, 0x11);
/* ICW2: Set vector offsets */
outb(0x21, 0x{:02X}); /* Master: IRQ0-7 -> INT 0x{:02X}-0x{:02X} */
outb(0xA1, 0x{:02X}); /* Slave: IRQ8-15 -> INT 0x{:02X}-0x{:02X} */
/* ICW3: Tell master about slave (IRQ2) and slave its identity */
outb(0x21, 0x04);
outb(0xA1, 0x02);
/* ICW4: 8086 mode */
outb(0x21, 0x01);
outb(0xA1, 0x01);
/* Mask all interrupts */
outb(0x21, 0xFF);
outb(0xA1, 0xFF);
}}
"#,
self.master_offset,
self.master_offset,
self.master_offset + 7,
self.slave_offset,
self.slave_offset,
self.slave_offset + 7,
)
}
pub fn enable_irq(mut self, irq: u8) -> Self {
let bit = 1u8 << (irq & 0x7);
if irq < 8 {
self.master_mask &= !bit;
} else {
self.slave_mask &= !bit;
}
self
}
pub fn disable_irq(mut self, irq: u8) -> Self {
let bit = 1u8 << (irq & 0x7);
if irq < 8 {
self.master_mask |= bit;
} else {
self.slave_mask |= bit;
}
self
}
pub fn generate_eoi_asm(&self, irq: u8) -> String {
let mut asm = String::new();
if irq >= 8 {
asm.push_str(" mov al, 0x20\n");
asm.push_str(" out 0xA0, al ; EOI to slave PIC\n");
}
asm.push_str(" mov al, 0x20\n");
asm.push_str(" out 0x20, al ; EOI to master PIC\n");
asm
}
}
impl Default for PicController {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct PitConfig {
pub base_frequency: u32,
pub target_frequency: u32,
pub channel: u8,
pub mode: u8,
}
impl PitConfig {
pub const PIT_BASE_FREQ: u32 = 1193182;
pub fn rate_generator(target_hz: u32) -> Self {
Self {
base_frequency: Self::PIT_BASE_FREQ,
target_frequency: target_hz,
channel: 0,
mode: 2,
}
}
pub fn reload_value(&self) -> u16 {
let div = self.base_frequency / self.target_frequency;
if div > 65535 {
65535
} else if div < 2 {
2
} else {
div as u16
}
}
pub fn generate_init_asm(&self) -> String {
let reload = self.reload_value();
let command = (self.channel << 6) | (0x3 << 4) | (self.mode << 1) | 0;
let mut asm = String::new();
asm.push_str("/* 8253 PIT Initialization — generated by llvm-native */\n");
asm.push_str(&format!(
" ; Channel {}, Mode {}, Target {} Hz (reload = {})\n",
self.channel, self.mode, self.target_frequency, reload
));
asm.push_str(&format!(
" mov al, 0x{:02X} ; command byte\n",
command
));
asm.push_str(" out 0x43, al\n");
asm.push_str(&format!(
" mov al, 0x{:02X} ; reload low byte\n",
reload & 0xFF
));
asm.push_str(&format!(
" out 0x{:02X}, al\n",
0x40 + self.channel as u16
));
asm.push_str(&format!(
" mov al, 0x{:02X} ; reload high byte\n",
(reload >> 8) & 0xFF
));
asm.push_str(&format!(
" out 0x{:02X}, al\n",
0x40 + self.channel as u16
));
asm
}
pub fn generate_init_c(&self) -> String {
let reload = self.reload_value();
let command = (self.channel << 6) | (0x3 << 4) | (self.mode << 1) | 0;
format!(
r#"/* 8253 PIT Initialization — generated by llvm-native */
static inline void pit_init(void) {{
uint16_t reload = {}; /* for {} Hz */
outb(0x43, 0x{:02X});
outb(0x{:02X}, reload & 0xFF);
outb(0x{:02X}, (reload >> 8) & 0xFF);
}}
"#,
reload,
self.target_frequency,
command,
0x40u16 + self.channel as u16,
0x40u16 + self.channel as u16,
)
}
}
impl Default for PitConfig {
fn default() -> Self {
Self::rate_generator(1000) }
}
#[derive(Debug, Clone)]
pub struct VgaConsole {
pub video_memory: u64,
pub width: u8,
pub height: u8,
pub cursor_x: u8,
pub cursor_y: u8,
pub color: u8,
}
impl VgaConsole {
pub const COLOR_BLACK: u8 = 0;
pub const COLOR_BLUE: u8 = 1;
pub const COLOR_GREEN: u8 = 2;
pub const COLOR_CYAN: u8 = 3;
pub const COLOR_RED: u8 = 4;
pub const COLOR_MAGENTA: u8 = 5;
pub const COLOR_BROWN: u8 = 6;
pub const COLOR_LIGHT_GREY: u8 = 7;
pub const COLOR_DARK_GREY: u8 = 8;
pub const COLOR_LIGHT_BLUE: u8 = 9;
pub const COLOR_LIGHT_GREEN: u8 = 10;
pub const COLOR_LIGHT_CYAN: u8 = 11;
pub const COLOR_LIGHT_RED: u8 = 12;
pub const COLOR_LIGHT_MAGENTA: u8 = 13;
pub const COLOR_YELLOW: u8 = 14;
pub const COLOR_WHITE: u8 = 15;
pub fn new() -> Self {
Self {
video_memory: 0xB8000,
width: 80,
height: 25,
cursor_x: 0,
cursor_y: 0,
color: Self::make_color(Self::COLOR_LIGHT_GREY, Self::COLOR_BLACK),
}
}
pub const fn make_color(fg: u8, bg: u8) -> u8 {
fg | (bg << 4)
}
pub fn generate_init_asm(&self) -> String {
let mut asm = String::new();
asm.push_str("/* VGA Console Initialization — generated by llvm-native */\n");
asm.push_str(&format!(
" ; Initialize VGA text mode console at 0x{:X}\n",
self.video_memory
));
asm.push_str("vga_init:\n");
asm.push_str(" ; Clear the screen (80x25 = 2000 characters, 4000 bytes)\n");
asm.push_str(" mov edi, 0xB8000\n");
asm.push_str(" mov ecx, 2000\n");
asm.push_str(&format!(
" mov ax, 0x{:02X}20 ; space + color\n",
self.color
));
asm.push_str(" rep stosw\n");
asm.push_str(" ret\n");
asm
}
pub fn generate_c_driver(&self) -> String {
format!(
r#"/* VGA Text Mode Console Driver — generated by llvm-native */
#include <stdint.h>
#define VGA_WIDTH {}
#define VGA_HEIGHT {}
#define VGA_MEMORY ((volatile uint16_t *)0xB8000)
static uint8_t vga_x = 0;
static uint8_t vga_y = 0;
static uint8_t vga_color = 0x{:02X};
static inline uint8_t vga_entry_color(uint8_t fg, uint8_t bg) {{
return fg | (bg << 4);
}}
static inline uint16_t vga_entry(unsigned char c, uint8_t color) {{
return (uint16_t)c | ((uint16_t)color << 8);
}}
static void vga_scroll(void) {{
for (int y = 1; y < VGA_HEIGHT; y++) {{
for (int x = 0; x < VGA_WIDTH; x++) {{
VGA_MEMORY[(y - 1) * VGA_WIDTH + x] = VGA_MEMORY[y * VGA_WIDTH + x];
}}
}}
for (int x = 0; x < VGA_WIDTH; x++) {{
VGA_MEMORY[(VGA_HEIGHT - 1) * VGA_WIDTH + x] = vga_entry(' ', vga_color);
}}
}}
void vga_putchar(char c) {{
if (c == '\n') {{
vga_x = 0;
vga_y++;
}} else {{
VGA_MEMORY[vga_y * VGA_WIDTH + vga_x] = vga_entry(c, vga_color);
vga_x++;
if (vga_x >= VGA_WIDTH) {{
vga_x = 0;
vga_y++;
}}
}}
if (vga_y >= VGA_HEIGHT) {{
vga_scroll();
vga_y = VGA_HEIGHT - 1;
}}
}}
void vga_clear(void) {{
for (int i = 0; i < VGA_WIDTH * VGA_HEIGHT; i++) {{
VGA_MEMORY[i] = vga_entry(' ', vga_color);
}}
vga_x = 0;
vga_y = 0;
}}
"#,
self.width, self.height, self.color
)
}
}
impl Default for VgaConsole {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct SerialConsole {
pub port_base: u16,
pub baud_divisor: u16,
pub data_bits: u8,
pub stop_bits: u8,
pub parity: u8,
pub interrupt_enable: u8,
pub fifo_control: u8,
}
impl SerialConsole {
pub const COM1: u16 = 0x3F8;
pub const COM2: u16 = 0x2F8;
pub const COM3: u16 = 0x3E8;
pub const COM4: u16 = 0x2E8;
pub fn com1_115200() -> Self {
Self {
port_base: Self::COM1,
baud_divisor: 1, data_bits: 8,
stop_bits: 1,
parity: 0,
interrupt_enable: 0x00,
fifo_control: 0xC7, }
}
pub fn com1_9600() -> Self {
Self {
port_base: Self::COM1,
baud_divisor: 12, data_bits: 8,
stop_bits: 1,
parity: 0,
interrupt_enable: 0x00,
fifo_control: 0xC7,
}
}
pub fn baud_rate_to_divisor(baud: u32) -> u16 {
if baud == 0 {
return 1;
}
(115200u32 / baud) as u16
}
pub fn generate_init_asm(&self) -> String {
let line_control = self.data_bits - 5
| (if self.stop_bits == 2 { 0x04 } else { 0x00 })
| (self.parity << 3);
let mut asm = String::new();
asm.push_str("/* Serial Port (UART) Initialization — generated by llvm-native */\n");
asm.push_str(&format!(" ; COM port: 0x{:04X}\n", self.port_base));
asm.push_str(&format!(" ; Baud divisor: {}\n", self.baud_divisor));
asm.push_str("serial_init:\n");
asm.push_str(&format!(" mov dx, 0x{:04X}\n", self.port_base + 3));
asm.push_str(" mov al, 0x80 ; enable DLAB\n");
asm.push_str(" out dx, al\n");
asm.push_str(&format!(" mov dx, 0x{:04X}\n", self.port_base));
asm.push_str(&format!(
" mov al, 0x{:02X} ; divisor low\n",
self.baud_divisor & 0xFF
));
asm.push_str(" out dx, al\n");
asm.push_str(&format!(" mov dx, 0x{:04X}\n", self.port_base + 1));
asm.push_str(&format!(
" mov al, 0x{:02X} ; divisor high\n",
(self.baud_divisor >> 8) & 0xFF
));
asm.push_str(" out dx, al\n");
asm.push_str(&format!(" mov dx, 0x{:04X}\n", self.port_base + 3));
asm.push_str(&format!(
" mov al, 0x{:02X} ; line control\n",
line_control
));
asm.push_str(" out dx, al\n");
asm.push_str(&format!(" mov dx, 0x{:04X}\n", self.port_base + 1));
asm.push_str(&format!(
" mov al, 0x{:02X} ; interrupt enable\n",
self.interrupt_enable
));
asm.push_str(" out dx, al\n");
asm.push_str(&format!(" mov dx, 0x{:04X}\n", self.port_base + 2));
asm.push_str(&format!(
" mov al, 0x{:02X} ; FIFO control\n",
self.fifo_control
));
asm.push_str(" out dx, al\n");
asm.push_str(" ret\n");
asm
}
pub fn generate_c_driver(&self) -> String {
let line_control = self.data_bits - 5
| (if self.stop_bits == 2 { 0x04 } else { 0x00 })
| (self.parity << 3);
format!(
r#"/* Serial Port Console Driver — generated by llvm-native */
#include <stdint.h>
#define SERIAL_PORT 0x{:04X}
static inline int serial_is_transmit_empty(void) {{
uint8_t status;
__asm__ volatile("inb %1, %0" : "=a"(status) : "Nd"((uint16_t)(SERIAL_PORT + 5)));
return status & 0x20;
}}
static inline void serial_write_byte(uint8_t c) {{
while (!serial_is_transmit_empty()) {{ __asm__ volatile("pause"); }}
__asm__ volatile("outb %0, %1" :: "a"(c), "Nd"((uint16_t)SERIAL_PORT));
}}
void serial_init(void) {{
/* Disable interrupts */
outb(SERIAL_PORT + 1, 0x00);
/* Set baud rate divisor */
outb(SERIAL_PORT + 3, 0x80); /* Enable DLAB */
outb(SERIAL_PORT + 0, 0x{:02X}); /* Divisor low */
outb(SERIAL_PORT + 1, 0x{:02X}); /* Divisor high */
/* Line control: {} data bits, {} stop bit(s), {} parity */
outb(SERIAL_PORT + 3, 0x{:02X});
/* Enable FIFO, clear, 14-byte threshold */
outb(SERIAL_PORT + 2, 0x{:02X});
}}
void serial_puts(const char *s) {{
while (*s) serial_write_byte(*s++);
}}
"#,
self.port_base,
self.baud_divisor & 0xFF,
(self.baud_divisor >> 8) & 0xFF,
self.data_bits,
self.stop_bits,
self.parity,
line_control,
self.fifo_control,
)
}
}
impl Default for SerialConsole {
fn default() -> Self {
Self::com1_115200()
}
}
#[derive(Debug, Clone)]
pub enum X86DebugChannel {
Serial(SerialConsole),
Vga(VgaConsole),
BochsPort,
Both(Box<SerialConsole>, Box<VgaConsole>),
None,
}
impl X86DebugChannel {
pub fn bochs() -> Self {
Self::BochsPort
}
pub fn serial() -> Self {
Self::Serial(SerialConsole::com1_115200())
}
pub fn vga() -> Self {
Self::Vga(VgaConsole::new())
}
pub fn generate_bochs_write_asm(&self) -> String {
" ; Write character to Bochs/QEMU debug port (0xE9)\n outb %al, $0xE9\n".to_string()
}
pub fn generate_bochs_write_c(&self) -> String {
r#"/* Bochs/QEMU E9 debug port output */
static inline void debug_putchar(char c) {
__asm__ volatile("outb %0, $0xE9" :: "a"(c));
}
static void debug_puts(const char *s) {
while (*s) debug_putchar(*s++);
}
"#
.to_string()
}
pub fn generate_init_code(&self) -> String {
match self {
Self::Serial(serial) => {
let mut code = serial.generate_init_asm();
code.push_str(&serial.generate_c_driver());
code
}
Self::Vga(vga) => {
let mut code = vga.generate_init_asm();
code.push_str(&vga.generate_c_driver());
code
}
Self::BochsPort => self.generate_bochs_write_c(),
Self::Both(serial, vga) => {
let mut code = serial.generate_init_asm();
code.push_str(&vga.generate_init_asm());
code.push_str(&serial.generate_c_driver());
code.push_str(&vga.generate_c_driver());
code
}
Self::None => String::new(),
}
}
}
impl Default for X86DebugChannel {
fn default() -> Self {
Self::serial()
}
}
#[derive(Debug, Clone)]
pub struct X86FpuContext {
pub save_fpu: bool,
pub save_sse: bool,
pub save_avx: bool,
pub save_avx512: bool,
pub save_area_size: usize,
pub use_xsave: bool,
pub xsave_features: u64,
}
impl X86FpuContext {
pub fn fxsave() -> Self {
Self {
save_fpu: true,
save_sse: true,
save_avx: false,
save_avx512: false,
save_area_size: 512,
use_xsave: false,
xsave_features: 0x03, }
}
pub fn xsave_avx() -> Self {
Self {
save_fpu: true,
save_sse: true,
save_avx: true,
save_avx512: false,
save_area_size: 1024,
use_xsave: true,
xsave_features: 0x07, }
}
pub fn xsave_avx512() -> Self {
Self {
save_fpu: true,
save_sse: true,
save_avx: true,
save_avx512: true,
save_area_size: 2688,
use_xsave: true,
xsave_features: 0xE7, }
}
pub fn generate_save_asm(&self) -> String {
if self.use_xsave {
let mut asm = String::new();
asm.push_str("/* XSAVE context save — generated by llvm-native */\n");
asm.push_str(&format!(
" movl $0x{:X}, %eax ; XSAVE feature mask (low)\n",
self.xsave_features as u32
));
asm.push_str(&format!(
" movl $0x{:X}, %edx ; XSAVE feature mask (high)\n",
(self.xsave_features >> 32) as u32
));
asm.push_str(" xsave (%rdi) ; save to buffer in RDI\n");
asm
} else {
" fxsave (%rdi) ; save FPU/SSE context\n".to_string()
}
}
pub fn generate_restore_asm(&self) -> String {
if self.use_xsave {
let mut asm = String::new();
asm.push_str("/* XRSTOR context restore — generated by llvm-native */\n");
asm.push_str(&format!(
" movl $0x{:X}, %eax ; XSAVE feature mask (low)\n",
self.xsave_features as u32
));
asm.push_str(&format!(
" movl $0x{:X}, %edx ; XSAVE feature mask (high)\n",
(self.xsave_features >> 32) as u32
));
asm.push_str(" xrstor (%rdi) ; restore from buffer in RDI\n");
asm
} else {
" fxrstor (%rdi) ; restore FPU/SSE context\n".to_string()
}
}
pub fn generate_c_context(&self) -> String {
let size = self.save_area_size;
format!(
r#"/* FPU Context Save/Restore — generated by llvm-native */
#include <stdint.h>
#define FPU_CTX_SIZE {}
typedef struct {{
uint8_t data[FPU_CTX_SIZE];
}} __attribute__((aligned(64))) fpu_context_t;
static inline void fpu_save(fpu_context_t *ctx) {{
__asm__ volatile("fxsave %0" :: "m"(*ctx) : "memory");
}}
static inline void fpu_restore(const fpu_context_t *ctx) {{
__asm__ volatile("fxrstor %0" :: "m"(*ctx) : "memory");
}}
"#,
size
)
}
}
impl Default for X86FpuContext {
fn default() -> Self {
Self::fxsave()
}
}
#[derive(Debug, Clone, Copy)]
pub struct GdtEntry {
pub raw: u64,
}
impl GdtEntry {
pub fn null() -> Self {
Self { raw: 0 }
}
pub fn code64_descriptor(dpl: u8) -> Self {
let d = (dpl as u64) & 0x3;
let raw = (d << 45) | (1u64 << 44) | (0xAu64 << 40) | (1u64 << 53) | (1u64 << 47) | (1u64 << 55); Self { raw }
}
pub fn code32_descriptor(dpl: u8) -> Self {
let d = (dpl as u64) & 0x3;
let raw = (d << 45) | (1u64 << 44) | (0xAu64 << 40) | (1u64 << 54) | (1u64 << 47) | (1u64 << 55) | 0xFFFF; Self { raw }
}
pub fn data64_descriptor(dpl: u8) -> Self {
let d = (dpl as u64) & 0x3;
let raw = (d << 45) | (1u64 << 44) | (0x2u64 << 40) | (1u64 << 47) | (1u64 << 55) | 0xFFFF; Self { raw }
}
pub fn data32_descriptor(dpl: u8) -> Self {
let d = (dpl as u64) & 0x3;
let raw = (d << 45) | (1u64 << 44) | (0x2u64 << 40) | (1u64 << 54) | (1u64 << 47) | (1u64 << 55) | 0xFFFF; Self { raw }
}
pub fn code16_descriptor() -> Self {
let raw = (1u64 << 44) | (0xAu64 << 40) | (1u64 << 47) | (1u64 << 55) | 0xFFFF; Self { raw }
}
pub fn data16_descriptor() -> Self {
let raw = (1u64 << 44) | (0x2u64 << 40) | (1u64 << 47) | (1u64 << 55) | 0xFFFF; Self { raw }
}
pub fn tss64_descriptor(base: u64, limit: u32, dpl: u8) -> (Self, Self) {
let d = (dpl as u64) & 0x3;
let raw0 = (base & 0xFFFF) | (((base >> 16) & 0xFF) << 32) | (((base >> 24) & 0xFF) << 56) | ((limit as u64 & 0xFFFF) << 16) | (0x9u64 << 40) | (d << 45) | (1u64 << 47); let raw1 = (base >> 32) as u64;
(Self { raw: raw0 }, Self { raw: raw1 })
}
}
#[derive(Debug, Clone, Copy)]
pub struct GdtDescriptor {
pub limit: u16,
pub base: u64,
}
impl GdtDescriptor {
pub fn new(base: u64, num_entries: u16) -> Self {
Self {
limit: num_entries.saturating_mul(8).saturating_sub(1),
base,
}
}
}
#[derive(Debug, Clone)]
pub struct X86GdtBuilder {
pub entries: Vec<GdtEntry>,
pub is_64bit: bool,
}
impl X86GdtBuilder {
pub fn new(is_64bit: bool) -> Self {
let mut gdt = Self {
entries: Vec::new(),
is_64bit,
};
gdt.entries.push(GdtEntry::null());
gdt
}
pub fn standard_64bit() -> Self {
let mut gdt = Self::new(true);
gdt.entries.push(GdtEntry::code64_descriptor(0)); gdt.entries.push(GdtEntry::data64_descriptor(0)); gdt.entries.push(GdtEntry::code64_descriptor(3)); gdt.entries.push(GdtEntry::data64_descriptor(3)); gdt
}
pub fn standard_32bit() -> Self {
let mut gdt = Self::new(false);
gdt.entries.push(GdtEntry::code32_descriptor(0)); gdt.entries.push(GdtEntry::data32_descriptor(0)); gdt.entries.push(GdtEntry::code32_descriptor(3)); gdt.entries.push(GdtEntry::data32_descriptor(3)); gdt
}
pub fn add_entry(mut self, entry: GdtEntry) -> Self {
self.entries.push(entry);
self
}
pub fn add_tss64(mut self, base: u64, limit: u32, dpl: u8) -> (Self, u16) {
let selector = (self.entries.len() * 8) as u16;
let (low, high) = GdtEntry::tss64_descriptor(base, limit, dpl);
self.entries.push(low);
self.entries.push(high);
(self, selector)
}
pub fn generate_asm(&self) -> String {
let mut asm = String::new();
asm.push_str("/* Global Descriptor Table — generated by llvm-native */\n");
asm.push_str(" .section .data.gdt\n");
asm.push_str(" .align 16\n");
asm.push_str(" .global gdt\n");
asm.push_str("gdt:\n");
for (i, entry) in self.entries.iter().enumerate() {
asm.push_str(&format!(
" .quad 0x{:016X} /* entry {} (selector 0x{:04X}) */\n",
entry.raw,
i,
i * 8
));
}
asm.push_str(&format!(" .set gdt_end, .\n"));
asm.push_str("\n .align 8\n");
asm.push_str(" .global gdt_descriptor\n");
asm.push_str("gdt_descriptor:\n");
let limit = (self.entries.len() * 8 - 1) as u16;
asm.push_str(&format!(" .short {}\n", limit));
asm.push_str(" .quad gdt\n");
asm
}
}
impl Default for X86GdtBuilder {
fn default() -> Self {
Self::standard_64bit()
}
}
#[derive(Debug, Clone)]
pub struct X86TaskStateSegment {
pub ist: [u64; 7],
pub io_map_base: u16,
pub reserved: [u32; 3],
}
impl X86TaskStateSegment {
pub fn new() -> Self {
Self {
ist: [0; 7],
io_map_base: 0x68, reserved: [0; 3],
}
}
pub fn set_ist(mut self, index: usize, stack_ptr: u64) -> Self {
if index > 0 && index <= 7 {
self.ist[index - 1] = stack_ptr;
}
self
}
pub fn generate_asm(&self) -> String {
let mut asm = String::new();
asm.push_str("/* 64-bit Task State Segment — generated by llvm-native */\n");
asm.push_str(" .section .data.tss\n");
asm.push_str(" .align 16\n");
asm.push_str(" .global tss\n");
asm.push_str("tss:\n");
for i in 0..7 {
asm.push_str(&format!(
" .quad 0x{:016X} /* IST{} */\n",
self.ist[i],
i + 1
));
}
asm.push_str(" .quad 0 /* reserved */\n");
asm.push_str(" .quad 0 /* reserved */\n");
asm.push_str(" .quad 0 /* reserved */\n");
asm.push_str(&format!(
" .short 0x{:04X} /* I/O map base */\n",
self.io_map_base
));
asm.push_str("tss_end:\n");
asm
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(104);
buf.extend_from_slice(&[0u8; 4]);
for _ in 0..3 {
buf.extend_from_slice(&[0u8; 8]);
}
for &ist in &self.ist {
buf.extend_from_slice(&ist.to_le_bytes());
}
buf.extend_from_slice(&[0u8; 16]);
buf.extend_from_slice(&self.io_map_base.to_le_bytes());
buf.extend_from_slice(&[0u8; 2]);
buf
}
}
impl Default for X86TaskStateSegment {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Default)]
pub struct X86EmbeddedBuilder {
inner: X86Embedded,
}
impl X86EmbeddedBuilder {
pub fn new() -> Self {
Self {
inner: X86Embedded::new(),
}
}
pub fn bitness(mut self, b: X86Bitness) -> Self {
match b {
X86Bitness::Bits64 => {
self.inner.target.bitness = X86Bitness::Bits64;
self.inner.linker_script.bitness = X86Bitness::Bits64;
self.inner.paging.bitness = X86Bitness::Bits64;
self.inner.compiler_flags.push("-m64".into());
}
X86Bitness::Bits32 => {
self.inner.target.bitness = X86Bitness::Bits32;
self.inner.linker_script.bitness = X86Bitness::Bits32;
self.inner.paging.bitness = X86Bitness::Bits32;
self.inner.compiler_flags.push("-m32".into());
}
X86Bitness::Bits16 => {
self.inner.target.bitness = X86Bitness::Bits16;
self.inner.linker_script.bitness = X86Bitness::Bits16;
self.inner.paging.bitness = X86Bitness::Bits16;
self.inner.compiler_flags.push("-m16".into());
}
}
self
}
pub fn target_kind(mut self, kind: X86BareMetalTargetKind) -> Self {
match kind {
X86BareMetalTargetKind::BiosBoot => self.inner.target = X86BareMetalTarget::bios_boot(),
X86BareMetalTargetKind::UefiBoot => {
self.inner.target = X86BareMetalTarget::uefi_boot_64()
}
X86BareMetalTargetKind::Multiboot => {
self.inner.target = X86BareMetalTarget::multiboot()
}
X86BareMetalTargetKind::Multiboot2 => {
self.inner.target = X86BareMetalTarget::multiboot2()
}
X86BareMetalTargetKind::Coreboot => self.inner.target = X86BareMetalTarget::coreboot(),
X86BareMetalTargetKind::LinuxKernelModule => {
self.inner.target = X86BareMetalTarget::linux_kernel_module()
}
X86BareMetalTargetKind::EfiByteCode => {
self.inner.target = X86BareMetalTarget::efi_byte_code()
}
_ => {}
}
self
}
pub fn boot_loader_kind(mut self, kind: BootLoaderKind) -> Self {
self.inner.boot_loader = X86BootLoader::new(kind);
self
}
pub fn opt_level(mut self, level: &str) -> Self {
self.inner.opt_level = level.to_string();
self
}
pub fn debug_info(mut self, enabled: bool) -> Self {
self.inner.debug_info = enabled;
self
}
pub fn stack_size(mut self, bytes: u64) -> Self {
self.inner.freestanding.stack_size = bytes;
self.inner.linker_script.stack_size = bytes;
self
}
pub fn heap_size(mut self, bytes: u64) -> Self {
self.inner.freestanding.heap_size = bytes;
self.inner.linker_script.heap_size = bytes;
self
}
pub fn identity_map(mut self, bytes: u64) -> Self {
self.inner.paging.identity_map_bytes = bytes;
self
}
pub fn kernel_vbase(mut self, vbase: u64) -> Self {
self.inner.paging.kernel_vbase = vbase;
self
}
pub fn flag(mut self, flag: &str) -> Self {
self.inner.compiler_flags.push(flag.to_string());
self
}
pub fn linker_flag(mut self, flag: &str) -> Self {
self.inner.linker_flags.push(flag.to_string());
self
}
pub fn build(self) -> X86Embedded {
self.inner
}
}
#[cfg(test)]
mod extended_tests_v2 {
use super::*;
#[test]
fn test_cpuid_result_vendor_string() {
let r = CpuidResult::new(
0,
u32::from_le_bytes(*b"Genu"),
u32::from_le_bytes(*b"ineI"),
u32::from_le_bytes(*b"ntel"),
);
assert_eq!(r.vendor_string(), "GenuineIntel");
}
#[test]
fn test_cpuid_result_brand_string() {
let parts = [
CpuidResult::new(
u32::from_le_bytes(*b"Inte"),
u32::from_le_bytes(*b"l(R)"),
u32::from_le_bytes(*b" Cor"),
u32::from_le_bytes(*b"e(TM"),
),
CpuidResult::new(
u32::from_le_bytes(*b") i7"),
u32::from_le_bytes(*b"-970"),
u32::from_le_bytes(*b"0K C"),
u32::from_le_bytes(*b"PU @"),
),
CpuidResult::new(
u32::from_le_bytes(*b" 3.6"),
u32::from_le_bytes(*b"0GHz"),
0,
0,
),
];
let brand = CpuidResult::brand_string(&parts);
assert!(brand.contains("Intel"));
assert!(brand.contains("Core"));
}
#[test]
fn test_cpuid_features_default() {
let f = CpuidFeatures::default();
assert!(!f.sse);
assert!(!f.long_mode);
assert!(!f.has_avx512());
assert!(!f.is_x86_64_capable());
}
#[test]
fn test_cpuid_features_decode_sse() {
let results = vec![(
1u32,
CpuidResult::new(0, 0, 0, 0x0800_0000), )];
let f = CpuidFeatures::decode_standard(&results);
assert!(f.sse);
assert!(!f.sse2);
}
#[test]
fn test_cpuid_features_decode_sse2() {
let results = vec![(
1u32,
CpuidResult::new(0, 0, 0, 0x0400_0000), )];
let f = CpuidFeatures::decode_standard(&results);
assert!(!f.sse);
assert!(f.sse2);
}
#[test]
fn test_cpuid_features_decode_long_mode() {
let results = vec![(
0x80000001u32,
CpuidResult::new(0, 0, 0, 0x2000_0000), )];
let f = CpuidFeatures::decode_standard(&results);
assert!(f.long_mode);
assert!(f.is_x86_64_capable());
}
#[test]
fn test_cpuid_features_enabled_features() {
let mut f = CpuidFeatures::new();
f.sse = true;
f.sse2 = true;
f.avx = true;
let feats = f.enabled_features();
assert!(feats.contains(&"sse".to_string()));
assert!(feats.contains(&"sse2".to_string()));
assert!(feats.contains(&"avx".to_string()));
}
#[test]
fn test_msr_definitions_address() {
assert_eq!(X86Msr::Ia32Efer.address(), 0xC000_0080);
assert_eq!(X86Msr::Ia32Star.address(), 0xC000_0081);
assert_eq!(X86Msr::Ia32Lstar.address(), 0xC000_0082);
assert_eq!(X86Msr::Ia32ApicBase.address(), 0x001B);
assert_eq!(X86Msr::Ia32TimestampCounter.address(), 0x0010);
}
#[test]
fn test_msr_is_user_readable() {
assert!(X86Msr::Ia32TimestampCounter.is_user_readable());
assert!(X86Msr::Ia32FsBase.is_user_readable());
assert!(!X86Msr::Ia32Efer.is_user_readable());
}
#[test]
fn test_msr_accessor_generate_read_c() {
let acc = MsrAccessor::new(true, true);
let code = acc.generate_read_c(X86Msr::Ia32Efer);
assert!(code.contains("rdmsr_ia32_efer"));
assert!(code.contains("0xC0000080"));
}
#[test]
fn test_msr_accessor_generate_write_c() {
let acc = MsrAccessor::new(true, true);
let code = acc.generate_write_c(X86Msr::Ia32Star);
assert!(code.contains("wrmsr_ia32_star"));
assert!(code.contains("wrmsr"));
}
#[test]
fn test_io_port_addresses() {
assert_eq!(IoPort::PicMasterCommand.address(), 0x20);
assert_eq!(IoPort::PicMasterData.address(), 0x21);
assert_eq!(IoPort::PitChannel0.address(), 0x40);
assert_eq!(IoPort::Com1Base.address(), 0x3F8);
assert_eq!(IoPort::PciConfigAddress.address(), 0xCF8);
}
#[test]
fn test_io_port_generate_inb() {
let asm = IoPort::Com1Base.generate_inb();
assert!(asm.contains("inb"));
assert!(asm.contains("0x3F8"));
}
#[test]
fn test_io_port_generate_outb() {
let asm = IoPort::PicMasterCommand.generate_outb();
assert!(asm.contains("outb"));
assert!(asm.contains("0x20"));
}
#[test]
fn test_pic_controller_remap() {
let pic = PicController::new();
let asm = pic.generate_remap_asm();
assert!(asm.contains("0x11"));
assert!(asm.contains("0x20"));
assert!(asm.contains("0x21"));
assert!(asm.contains("0xA0"));
assert!(asm.contains("0xA1"));
let c = pic.generate_remap_c();
assert!(c.contains("pic_remap"));
assert!(c.contains("0x20"));
}
#[test]
fn test_pic_controller_enable_disable_irq() {
let pic = PicController::new().enable_irq(0).enable_irq(1);
assert_eq!(pic.master_mask & 0x03, 0); let pic = pic.disable_irq(0);
assert_ne!(pic.master_mask & 0x01, 0); }
#[test]
fn test_pit_config_reload_value() {
let pit = PitConfig::rate_generator(100);
let reload = pit.reload_value();
assert!(reload > 10000 && reload < 15000);
}
#[test]
fn test_pit_config_rate_generator_1000hz() {
let pit = PitConfig::rate_generator(1000);
let reload = pit.reload_value();
assert_eq!(reload, 1193); }
#[test]
fn test_pit_config_generate_init_asm() {
let pit = PitConfig::rate_generator(1000);
let asm = pit.generate_init_asm();
assert!(asm.contains("0x43"));
assert!(asm.contains("0x40"));
assert!(asm.contains("out"));
}
#[test]
fn test_pit_config_generate_init_c() {
let pit = PitConfig::rate_generator(100);
let c = pit.generate_init_c();
assert!(c.contains("pit_init"));
assert!(c.contains("outb"));
}
#[test]
fn test_vga_console_make_color() {
let c = VgaConsole::make_color(VgaConsole::COLOR_WHITE, VgaConsole::COLOR_BLUE);
assert_eq!(c, 0x1F); }
#[test]
fn test_vga_console_default() {
let vga = VgaConsole::default();
assert_eq!(vga.width, 80);
assert_eq!(vga.height, 25);
assert_eq!(vga.video_memory, 0xB8000);
}
#[test]
fn test_vga_console_generate_c_driver() {
let vga = VgaConsole::new();
let c = vga.generate_c_driver();
assert!(c.contains("VGA_WIDTH"));
assert!(c.contains("VGA_MEMORY"));
assert!(c.contains("vga_putchar"));
assert!(c.contains("vga_scroll"));
assert!(c.contains("vga_clear"));
}
#[test]
fn test_serial_console_com1_115200() {
let serial = SerialConsole::com1_115200();
assert_eq!(serial.port_base, 0x3F8);
assert_eq!(serial.baud_divisor, 1);
}
#[test]
fn test_serial_console_com1_9600() {
let serial = SerialConsole::com1_9600();
assert_eq!(serial.port_base, 0x3F8);
assert_eq!(serial.baud_divisor, 12);
}
#[test]
fn test_serial_console_baud_rate_to_divisor() {
assert_eq!(SerialConsole::baud_rate_to_divisor(115200), 1);
assert_eq!(SerialConsole::baud_rate_to_divisor(57600), 2);
assert_eq!(SerialConsole::baud_rate_to_divisor(38400), 3);
assert_eq!(SerialConsole::baud_rate_to_divisor(19200), 6);
assert_eq!(SerialConsole::baud_rate_to_divisor(9600), 12);
}
#[test]
fn test_serial_console_generate_init_asm() {
let serial = SerialConsole::com1_115200();
let asm = serial.generate_init_asm();
assert!(asm.contains("serial_init"));
assert!(asm.contains("0x03F8"));
assert!(asm.contains("0x80")); }
#[test]
fn test_serial_console_generate_c_driver() {
let serial = SerialConsole::com1_115200();
let c = serial.generate_c_driver();
assert!(c.contains("serial_init"));
assert!(c.contains("serial_is_transmit_empty"));
assert!(c.contains("serial_write_byte"));
assert!(c.contains("serial_puts"));
}
#[test]
fn test_debug_channel_bochs() {
let ch = X86DebugChannel::bochs();
let code = ch.generate_init_code();
assert!(code.contains("0xE9"));
}
#[test]
fn test_debug_channel_serial() {
let ch = X86DebugChannel::serial();
let code = ch.generate_init_code();
assert!(!code.is_empty());
}
#[test]
fn test_debug_channel_none() {
let ch = X86DebugChannel::None;
let code = ch.generate_init_code();
assert!(code.is_empty());
}
#[test]
fn test_fpu_context_fxsave() {
let fpu = X86FpuContext::fxsave();
assert_eq!(fpu.save_area_size, 512);
assert!(!fpu.use_xsave);
assert!(!fpu.save_avx);
assert!(!fpu.save_avx512);
let asm = fpu.generate_save_asm();
assert!(asm.contains("fxsave"));
}
#[test]
fn test_fpu_context_xsave_avx() {
let fpu = X86FpuContext::xsave_avx();
assert!(fpu.use_xsave);
assert!(fpu.save_avx);
assert!(!fpu.save_avx512);
let asm = fpu.generate_save_asm();
assert!(asm.contains("xsave"));
}
#[test]
fn test_fpu_context_xsave_avx512() {
let fpu = X86FpuContext::xsave_avx512();
assert!(fpu.save_avx512);
assert_eq!(fpu.save_area_size, 2688);
}
#[test]
fn test_fpu_context_c_generation() {
let fpu = X86FpuContext::fxsave();
let c = fpu.generate_c_context();
assert!(c.contains("fpu_context_t"));
assert!(c.contains("fpu_save"));
assert!(c.contains("fpu_restore"));
assert!(c.contains("fxsave"));
assert!(c.contains("fxrstor"));
}
#[test]
fn test_gdt_entry_null() {
let entry = GdtEntry::null();
assert_eq!(entry.raw, 0);
}
#[test]
fn test_gdt_entry_code64() {
let entry = GdtEntry::code64_descriptor(0);
assert_ne!(entry.raw, 0);
assert!(entry.raw & (1u64 << 53) != 0);
}
#[test]
fn test_gdt_entry_code32() {
let entry = GdtEntry::code32_descriptor(0);
assert_ne!(entry.raw, 0);
assert!(entry.raw & (1u64 << 54) != 0);
}
#[test]
fn test_gdt_builder_standard_64bit() {
let gdt = X86GdtBuilder::standard_64bit();
assert_eq!(gdt.entries.len(), 5); }
#[test]
fn test_gdt_builder_standard_32bit() {
let gdt = X86GdtBuilder::standard_32bit();
assert_eq!(gdt.entries.len(), 5); }
#[test]
fn test_gdt_builder_add_tss64() {
let gdt = X86GdtBuilder::standard_64bit();
let (gdt, selector) = gdt.add_tss64(0x1000, 103, 0);
assert_eq!(gdt.entries.len(), 7);
assert_eq!(selector, 5 * 8); }
#[test]
fn test_gdt_builder_generate_asm() {
let gdt = X86GdtBuilder::standard_64bit();
let asm = gdt.generate_asm();
assert!(asm.contains("gdt:"));
assert!(asm.contains("gdt_descriptor"));
assert!(asm.contains(".quad"));
}
#[test]
fn test_tss_new() {
let tss = X86TaskStateSegment::new();
assert_eq!(tss.ist.iter().sum::<u64>(), 0);
assert_eq!(tss.io_map_base, 0x68);
}
#[test]
fn test_tss_set_ist() {
let tss = X86TaskStateSegment::new()
.set_ist(1, 0x8000)
.set_ist(2, 0x9000)
.set_ist(7, 0x10000);
assert_eq!(tss.ist[0], 0x8000);
assert_eq!(tss.ist[1], 0x9000);
assert_eq!(tss.ist[6], 0x10000);
}
#[test]
fn test_tss_generate_asm() {
let tss = X86TaskStateSegment::new().set_ist(1, 0xDEAD);
let asm = tss.generate_asm();
assert!(asm.contains("tss:"));
assert!(asm.contains("IST1"));
assert!(asm.contains("0xDEAD"));
}
#[test]
fn test_tss_to_bytes() {
let tss = X86TaskStateSegment::new();
let bytes = tss.to_bytes();
assert_eq!(bytes.len(), 104);
}
#[test]
fn test_embedded_builder_default() {
let builder = X86EmbeddedBuilder::new();
let emb = builder.build();
assert_eq!(emb.opt_level, "2");
assert!(!emb.debug_info);
}
#[test]
fn test_embedded_builder_full_config() {
let emb = X86EmbeddedBuilder::new()
.bitness(X86Bitness::Bits64)
.target_kind(X86BareMetalTargetKind::Multiboot)
.boot_loader_kind(BootLoaderKind::MultibootV1)
.opt_level("3")
.debug_info(true)
.stack_size(32768)
.heap_size(2097152)
.identity_map(0x400000)
.kernel_vbase(0xFFFF_8000_0000_0000)
.flag("-fno-stack-protector")
.flag("-fno-omit-frame-pointer")
.linker_flag("-z max-page-size=0x1000")
.build();
assert_eq!(emb.opt_level, "3");
assert!(emb.debug_info);
assert_eq!(emb.freestanding.stack_size, 32768);
assert_eq!(emb.freestanding.heap_size, 2097152);
assert_eq!(emb.paging.identity_map_bytes, 0x400000);
assert_eq!(emb.paging.kernel_vbase, 0xFFFF_8000_0000_0000);
}
#[test]
fn test_embedded_builder_32bit() {
let emb = X86EmbeddedBuilder::new()
.bitness(X86Bitness::Bits32)
.build();
assert_eq!(emb.target.bitness, X86Bitness::Bits32);
assert!(emb.compiler_flags.contains(&"-m32".to_string()));
}
#[test]
fn test_gdt_descriptor() {
let desc = GdtDescriptor::new(0x8000, 5);
assert_eq!(desc.limit, 5 * 8 - 1);
assert_eq!(desc.base, 0x8000);
}
#[test]
fn test_all_cpuid_features_decode() {
let results = vec![
(
1u32,
CpuidResult::new(
0x000906EA, 0x00100800, 0x7FFAFBBF, 0xBFEBFBFF, ),
),
(
7u32,
CpuidResult::new(
0, 0x029C6FBF, 0x00000004, 0x00000000, ),
),
(
0x80000001u32,
CpuidResult::new(
0, 0, 0x00000121, 0x2C100800, ),
),
];
let f = CpuidFeatures::decode_standard(&results);
assert!(f.sse);
assert!(f.sse2);
assert!(f.sse3);
assert!(f.ssse3);
assert!(f.sse41);
assert!(f.sse42);
assert!(f.avx);
assert!(f.avx2);
assert!(f.fma);
assert!(f.aes);
assert!(f.long_mode);
assert!(f.smep);
assert!(f.smap);
assert!(f.umip);
assert!(f.is_x86_64_capable());
assert!(!f.avx512f);
}
#[test]
fn test_gdt_data_descriptors() {
let data64 = GdtEntry::data64_descriptor(0);
assert_ne!(data64.raw, 0);
let data32 = GdtEntry::data32_descriptor(0);
assert_ne!(data32.raw, 0);
assert!(data32.raw & (1u64 << 54) != 0); }
#[test]
fn test_pic_eoi() {
let pic = PicController::new();
let eoi = pic.generate_eoi_asm(0);
assert!(eoi.contains("out 0x20"));
let eoi_slave = pic.generate_eoi_asm(8);
assert!(eoi_slave.contains("out 0xA0"));
assert!(eoi_slave.contains("out 0x20"));
}
#[test]
fn test_builder_x86_embedded_bare_metal() {
let emb = X86EmbeddedBuilder::new()
.bitness(X86Bitness::Bits64)
.target_kind(X86BareMetalTargetKind::BareMetal)
.build();
let artifacts = emb.generate_all();
assert!(!artifacts.startup_asm.is_empty());
assert!(!artifacts.linker_script.is_empty());
assert!(artifacts.boot_sector.is_none());
}
#[test]
fn test_builder_x86_embedded_bios() {
let emb = X86EmbeddedBuilder::new()
.bitness(X86Bitness::Bits16)
.target_kind(X86BareMetalTargetKind::BiosBoot)
.boot_loader_kind(BootLoaderKind::BiosBootSector)
.build();
let artifacts = emb.generate_all();
assert!(artifacts.boot_sector.is_some());
}
#[test]
fn test_gdt_entry_tss64() {
let (low, high) = GdtEntry::tss64_descriptor(0x1000, 103, 0);
assert_ne!(low.raw, 0);
assert_eq!(high.raw, 0); }
#[test]
fn test_msr_name() {
assert_eq!(X86Msr::Ia32Efer.name(), "IA32_EFER");
assert_eq!(X86Msr::Ia32Star.name(), "IA32_STAR");
assert_eq!(X86Msr::Ia32SpecCtrl.name(), "IA32_SPEC_CTRL");
}
#[test]
fn test_io_port_name() {
assert_eq!(IoPort::PciConfigAddress.name(), "PCI Config Address");
assert_eq!(IoPort::A20FastGate.name(), "A20 Fast Gate");
}
#[test]
fn test_fpu_restore_asm() {
let fpu = X86FpuContext::fxsave();
let asm = fpu.generate_restore_asm();
assert!(asm.contains("fxrstor"));
let fpu_xsave = X86FpuContext::xsave_avx();
let asm_xsave = fpu_xsave.generate_restore_asm();
assert!(asm_xsave.contains("xrstor"));
}
#[test]
fn test_debug_channel_both() {
let serial = SerialConsole::com1_115200();
let vga = VgaConsole::new();
let ch = X86DebugChannel::Both(Box::new(serial), Box::new(vga));
let code = ch.generate_init_code();
assert!(!code.is_empty());
}
#[test]
fn test_full_kernel_bootstrap_64bit() {
let emb = X86EmbeddedBuilder::new()
.bitness(X86Bitness::Bits64)
.target_kind(X86BareMetalTargetKind::Multiboot)
.boot_loader_kind(BootLoaderKind::MultibootV1)
.opt_level("2")
.stack_size(0x10000)
.heap_size(0x100000)
.identity_map(0x400000)
.kernel_vbase(0xFFFF_8000_0000_0000)
.flag("-ffreestanding")
.flag("-fno-stack-protector")
.linker_flag("-z max-page-size=0x1000")
.build();
let artifacts = emb.generate_all();
assert!(artifacts.startup_asm.contains("_start"));
assert!(artifacts.crt0_source.contains("zero_bss"));
assert!(artifacts.linker_script.contains("ENTRY(_start)"));
}
#[test]
fn test_full_kernel_bootstrap_32bit() {
let emb = X86EmbeddedBuilder::new()
.bitness(X86Bitness::Bits32)
.target_kind(X86BareMetalTargetKind::BareMetal)
.opt_level("s")
.build();
let artifacts = emb.generate_all();
assert!(artifacts.startup_asm.contains("xorl %ebp, %ebp"));
}
#[test]
fn test_idt_coverage_all_exceptions() {
let mut ih = X86InterruptHandlers::new();
let all = [
ExceptionVector::DivideError,
ExceptionVector::Debug,
ExceptionVector::Nmi,
ExceptionVector::Breakpoint,
ExceptionVector::Overflow,
ExceptionVector::InvalidOpcode,
ExceptionVector::DoubleFault,
ExceptionVector::InvalidTss,
ExceptionVector::SegmentNotPresent,
ExceptionVector::StackSegmentFault,
ExceptionVector::GeneralProtectionFault,
ExceptionVector::PageFault,
ExceptionVector::X87FloatingPoint,
ExceptionVector::AlignmentCheck,
ExceptionVector::MachineCheck,
ExceptionVector::SimdFloatingPoint,
ExceptionVector::Virtualization,
ExceptionVector::ControlProtection,
];
for (i, ex) in all.iter().enumerate() {
ih.register_exception(*ex, 0x8000 + i as u64 * 16);
}
assert_eq!(ih.handler_count(), 18);
assert!(ih.generate_idt_asm().is_some());
}
#[test]
fn test_pit_prescaler_calculation() {
for hz in &[18u32, 50, 100, 250, 500, 1000, 2000] {
let pit = PitConfig::rate_generator(*hz);
let reload = pit.reload_value();
let actual = PitConfig::PIT_BASE_FREQ / reload as u32;
let diff = if actual > *hz {
actual - hz
} else {
hz - actual
};
let pct = (diff as f64) / (*hz as f64) * 100.0;
assert!(pct < 5.0, "PIT {}Hz diverges by {:.1}%", hz, pct);
}
}
#[test]
fn test_vga_color_constants() {
assert_eq!(VgaConsole::COLOR_BLACK, 0);
assert_eq!(VgaConsole::COLOR_WHITE, 15);
assert_eq!(VgaConsole::COLOR_BLUE, 1);
assert_eq!(VgaConsole::COLOR_GREEN, 2);
assert_eq!(VgaConsole::COLOR_RED, 4);
assert_eq!(VgaConsole::COLOR_CYAN, 3);
assert_eq!(VgaConsole::COLOR_MAGENTA, 5);
assert_eq!(VgaConsole::COLOR_YELLOW, 14);
}
#[test]
fn test_serial_com_ports() {
assert_eq!(SerialConsole::COM1, 0x3F8);
assert_eq!(SerialConsole::COM2, 0x2F8);
assert_eq!(SerialConsole::COM3, 0x3E8);
assert_eq!(SerialConsole::COM4, 0x2E8);
}
#[test]
fn test_msr_syscall_related() {
assert_eq!(X86Msr::Ia32Star.address(), 0xC000_0081);
assert_eq!(X86Msr::Ia32Lstar.address(), 0xC000_0082);
assert_eq!(X86Msr::Ia32Cstar.address(), 0xC000_0083);
assert_eq!(X86Msr::Ia32Fmask.address(), 0xC000_0084);
}
#[test]
fn test_acpi_all_known_signatures() {
assert_eq!(AcpiTableKind::from_signature(b"DSDT"), AcpiTableKind::Dsdt);
assert_eq!(AcpiTableKind::from_signature(b"SSDT"), AcpiTableKind::Ssdt);
assert_eq!(AcpiTableKind::from_signature(b"FACP"), AcpiTableKind::Fadt);
assert_eq!(AcpiTableKind::from_signature(b"APIC"), AcpiTableKind::Madt);
assert_eq!(AcpiTableKind::from_signature(b"MCFG"), AcpiTableKind::Mcfg);
assert_eq!(AcpiTableKind::from_signature(b"HPET"), AcpiTableKind::Hpet);
}
#[test]
fn test_embedded_artifacts_full_struct() {
let artifacts = X86EmbeddedArtifacts {
compiler_flags: vec!["-O2".into()],
linker_flags: vec!["-Tlinker.ld".into()],
linker_script: "MEMORY {}".into(),
boot_sector: Some(vec![0x55, 0xAA]),
startup_asm: "_start:".into(),
idt: Some("idt:".into()),
page_tables: Some("pml4:".into()),
crt0_source: "void _start() {}".into(),
};
assert_eq!(artifacts.compiler_flags.len(), 1);
assert!(artifacts.boot_sector.is_some());
assert!(artifacts.idt.is_some());
assert!(artifacts.page_tables.is_some());
}
#[test]
fn test_page_flags_all_variants() {
let rw = PageFlags::kernel_rw();
assert!(rw.present && rw.writable && !rw.no_execute);
let ro = PageFlags::kernel_ro();
assert!(!ro.writable);
let urw = PageFlags::user_rw();
assert!(urw.user_accessible);
let rxn = PageFlags::kernel_rw_nx();
assert!(rxn.no_execute && rxn.writable);
}
#[test]
fn test_gdt_all_entry_types_nonnull() {
assert!(GdtEntry::code64_descriptor(3).raw != 0);
assert!(GdtEntry::code32_descriptor(0).raw != 0);
assert!(GdtEntry::data64_descriptor(3).raw != 0);
assert!(GdtEntry::data32_descriptor(0).raw != 0);
assert!(GdtEntry::code16_descriptor().raw != 0);
assert!(GdtEntry::data16_descriptor().raw != 0);
assert_eq!(GdtEntry::null().raw, 0);
}
#[test]
fn test_pic_slave_irq() {
let pic = PicController::new()
.enable_irq(8)
.enable_irq(9)
.enable_irq(15);
assert_eq!(pic.slave_mask & 0xC1, 0);
}
#[test]
fn test_fpu_full_xsave_restore_cycle() {
let fpu = X86FpuContext::xsave_avx512();
let save = fpu.generate_save_asm();
let restore = fpu.generate_restore_asm();
assert!(save.contains("xsave"));
assert!(restore.contains("xrstor"));
}
}