From 42c9ba12dccb03c0080f3c47b578a52d1afe126d Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Sun, 14 Aug 2016 09:31:35 -0600 Subject: [PATCH] Remove interrupt setup from asm bootloader, add io module, memcpy functions, and serial driver --- Cargo.toml | 3 + Makefile | 6 +- bootloader/x86/interrupts-i386.asm | 105 ---------------------- bootloader/x86/interrupts-x86_64.asm | 130 --------------------------- bootloader/x86/startup-i386.asm | 2 - bootloader/x86/startup-x86_64.asm | 7 +- src/arch/mod.rs | 3 + src/arch/x86_64/idt.rs | 42 ++++++--- src/arch/x86_64/io/io.rs | 61 +++++++++++++ src/arch/x86_64/io/mmio.rs | 31 +++++++ src/arch/x86_64/io/mod.rs | 9 ++ src/arch/x86_64/io/pio.rs | 83 +++++++++++++++++ src/arch/x86_64/main.rs | 58 ++++++++++-- src/arch/x86_64/mem.rs | 70 +++++++++++++++ src/arch/x86_64/mod.rs | 12 +++ src/arch/x86_64/serial.rs | 39 ++++++++ src/lib.rs | 25 ++++++ 17 files changed, 422 insertions(+), 264 deletions(-) delete mode 100644 bootloader/x86/interrupts-i386.asm delete mode 100644 bootloader/x86/interrupts-x86_64.asm create mode 100644 src/arch/x86_64/io/io.rs create mode 100644 src/arch/x86_64/io/mmio.rs create mode 100644 src/arch/x86_64/io/mod.rs create mode 100644 src/arch/x86_64/io/pio.rs create mode 100644 src/arch/x86_64/mem.rs create mode 100644 src/arch/x86_64/serial.rs diff --git a/Cargo.toml b/Cargo.toml index 949aac2..577a595 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,9 @@ version = "0.1.0" [lib] crate-type = ["staticlib"] +[dependencies] +bitflags = "*" + [profile.dev] panic = "abort" diff --git a/Makefile b/Makefile index 5edd574..e25521b 100644 --- a/Makefile +++ b/Makefile @@ -10,16 +10,16 @@ bochs: build/harddrive.bin bochs -f bochs.$(ARCH) qemu: build/harddrive.bin - qemu-system-$(ARCH) -drive file=$<,format=raw,index=0,media=disk + qemu-system-$(ARCH) -serial mon:stdio -drive file=$<,format=raw,index=0,media=disk FORCE: build/libkernel.a: FORCE - rustc --crate-type staticlib -C lto -O src/lib.rs -o $@ + cargo rustc -- -C lto -o $@ #--target $(ARCH)-unknown-none.json build/kernel.bin: build/libkernel.a - ld -m elf_$(ARCH) -o $@ -T bootloader/x86/kernel.ld -z max-page-size=0x1000 $< + ld -m elf_$(ARCH) --gc-sections -z max-page-size=0x1000 -T bootloader/x86/kernel.ld -o $@ $< build/kernel.list: build/kernel.bin objdump -C -M intel -D $< > $@ diff --git a/bootloader/x86/interrupts-i386.asm b/bootloader/x86/interrupts-i386.asm deleted file mode 100644 index 5fab08a..0000000 --- a/bootloader/x86/interrupts-i386.asm +++ /dev/null @@ -1,105 +0,0 @@ -struc IDTEntry - .offsetl resw 1 - .selector resw 1 - .zero resb 1 - .attribute resb 1 - .offseth resw 1 -endstruc - -SECTION .text -USE32 - -interrupts: -.first: - mov [.entry], byte 0 - jmp dword .handle -.second: -%assign i 1 -%rep 255 - mov [.entry], byte i - jmp dword .handle -%assign i i+1 -%endrep -.handle: - push ebp - push esi - push edi - push edx - push ecx - push ebx - push eax - - push esp - push dword [.entry] - - mov eax, gdt.kernel_data - mov ds, eax - mov es, eax - mov fs, eax - mov gs, eax - - call dword [.handler] - - mov eax, gdt.user_data | 3 - mov ds, eax - mov es, eax - mov fs, eax - mov eax, gdt.user_tls | 3 - mov gs, eax - - add esp, 8 ; Skip interrupt code and reg pointer - - pop eax - pop ebx - pop ecx - pop edx - pop edi - pop esi - pop ebp - - iretd - -.handler: dd 0 -.entry: dd 0 - -idtr: - dw (idt.end - idt) + 1 - dd idt - -idt: -%assign i 0 - -;Below system call -%rep 128 - istruc IDTEntry - at IDTEntry.offsetl, dw interrupts+(interrupts.second-interrupts.first)*i - at IDTEntry.selector, dw gdt.kernel_code - at IDTEntry.zero, db 0 - at IDTEntry.attribute, db attrib.present | attrib.interrupt32 - at IDTEntry.offseth, dw 0 - iend -%assign i i+1 -%endrep - -;System call -istruc IDTEntry - at IDTEntry.offsetl, dw interrupts+(interrupts.second-interrupts.first)*i - at IDTEntry.selector, dw gdt.kernel_code - at IDTEntry.zero, db 0 - at IDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.interrupt32 - at IDTEntry.offseth, dw 0 -iend -%assign i i+1 - -;Above system call -%rep 127 - istruc IDTEntry - at IDTEntry.offsetl, dw interrupts+(interrupts.second-interrupts.first)*i - at IDTEntry.selector, dw gdt.kernel_code - at IDTEntry.zero, db 0 - at IDTEntry.attribute, db attrib.present | attrib.interrupt32 - at IDTEntry.offseth, dw 0 - iend -%assign i i+1 -%endrep -.end: diff --git a/bootloader/x86/interrupts-x86_64.asm b/bootloader/x86/interrupts-x86_64.asm deleted file mode 100644 index c2305f6..0000000 --- a/bootloader/x86/interrupts-x86_64.asm +++ /dev/null @@ -1,130 +0,0 @@ -struc IDTEntry - .offsetl resw 1 - .selector resw 1 - .ist resb 1 - .attribute resb 1 - .offsetm resw 1 - .offseth resd 1 - .reserved resd 1 -endstruc - -SECTION .text -USE64 -interrupts: -.first: - mov [.entry], byte 0 - jmp qword .handle -.second: -%assign i 1 -%rep 255 - mov [.entry], byte i - jmp qword .handle -%assign i i+1 -%endrep -.handle: - push rbp - push r15 - push r14 - push r13 - push r12 - push r11 - push r10 - push r9 - push r8 - push rsi - push rdi - push rdx - push rcx - push rbx - push rax - - mov rsi, rsp - push rsi - mov rdi, qword [.entry] - push rdi - - mov rax, gdt.kernel_data - mov ds, rax - mov es, rax - mov fs, rax - mov gs, rax - - call qword [.handler] - - mov rax, gdt.user_data | 3 - mov ds, rax - mov es, rax - mov gs, rax - mov rax, gdt.user_tls | 3 - mov fs, rax - - add rsp, 16 ; Skip interrupt code and reg pointer - - pop rax - pop rbx - pop rcx - pop rdx - pop rdi - pop rsi - pop r8 - pop r9 - pop r10 - pop r11 - pop r12 - pop r13 - pop r14 - pop r15 - pop rbp - - iretq - -.handler: dq 0 -.entry: dq 0 - -idtr: - dw (idt.end - idt) + 1 - dq idt - -idt: -%assign i 0 - -;Below syscall -%rep 128 - istruc IDTEntry - at IDTEntry.offsetl, dw interrupts+(interrupts.second-interrupts.first)*i - at IDTEntry.selector, dw gdt.kernel_code - at IDTEntry.ist, db 0 - at IDTEntry.attribute, db attrib.present | attrib.interrupt64 - at IDTEntry.offsetm, dw 0 - at IDTEntry.offseth, dd 0 - at IDTEntry.reserved, dd 0 - iend -%assign i i+1 -%endrep - -;Syscall -istruc IDTEntry - at IDTEntry.offsetl, dw interrupts+(interrupts.second-interrupts.first)*i - at IDTEntry.selector, dw gdt.kernel_code - at IDTEntry.ist, db 0 - at IDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.interrupt64 - at IDTEntry.offsetm, dw 0 - at IDTEntry.offseth, dd 0 - at IDTEntry.reserved, dd 0 -iend -%assign i i+1 - -;Above syscall -%rep 127 - istruc IDTEntry - at IDTEntry.offsetl, dw interrupts+(interrupts.second-interrupts.first)*i - at IDTEntry.selector, dw gdt.kernel_code - at IDTEntry.ist, db 0 - at IDTEntry.attribute, db attrib.present | attrib.interrupt64 - at IDTEntry.offsetm, dw 0 - at IDTEntry.offseth, dd 0 - at IDTEntry.reserved, dd 0 - iend -%assign i i+1 -%endrep -.end: diff --git a/bootloader/x86/startup-i386.asm b/bootloader/x86/startup-i386.asm index 2466346..c38a09e 100644 --- a/bootloader/x86/startup-i386.asm +++ b/bootloader/x86/startup-i386.asm @@ -146,5 +146,3 @@ tss: at TSS.iomap_base, dw 0xFFFF iend .end: - -%include "interrupts-i386.asm" diff --git a/bootloader/x86/startup-x86_64.asm b/bootloader/x86/startup-x86_64.asm index 05b7021..2ced709 100644 --- a/bootloader/x86/startup-x86_64.asm +++ b/bootloader/x86/startup-x86_64.asm @@ -73,11 +73,8 @@ long_mode: xor rax, rax mov eax, [kernel_base + 0x18] mov rbx, gdtr + xchg bx, bx jmp rax -.lp: - sti - hlt - jmp .lp gdtr: dw gdt.end + 1 ; size @@ -179,5 +176,3 @@ long_mode: at TSS.iomap_base, dw 0xFFFF iend .end: - - %include "interrupts-x86_64.asm" diff --git a/src/arch/mod.rs b/src/arch/mod.rs index 37a90c2..924dc1a 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -1,4 +1,7 @@ //! Architecture specific items +#[cfg(target_arch = "x86_64")] +pub use self::x86_64::*; + #[cfg(target_arch = "x86_64")] pub mod x86_64; diff --git a/src/arch/x86_64/idt.rs b/src/arch/x86_64/idt.rs index 7b6e021..7cc422a 100644 --- a/src/arch/x86_64/idt.rs +++ b/src/arch/x86_64/idt.rs @@ -7,10 +7,23 @@ pub static mut IDTR: IdtDescriptor = IdtDescriptor { pub static mut IDT: [IdtEntry; 256] = [IdtEntry::new(); 256]; +bitflags! { + pub flags IdtFlags: u8 { + const IDT_PRESENT = 1 << 7, + const IDT_RING_0 = 0 << 5, + const IDT_RING_1 = 1 << 5, + const IDT_RING_2 = 2 << 5, + const IDT_RING_3 = 3 << 5, + const IDT_SS = 1 << 4, + const IDT_INTERRUPT = 0xE, + const IDT_TRAP = 0xF, + } +} + #[repr(packed)] pub struct IdtDescriptor { - pub size: u16, - pub offset: u64 + size: u16, + offset: u64 } impl IdtDescriptor { @@ -18,18 +31,22 @@ impl IdtDescriptor { self.size = (slice.len() * mem::size_of::() - 1) as u16; self.offset = slice.as_ptr() as u64; } + + pub unsafe fn load(&self) { + asm!("lidt [rax]" : : "{rax}"(self as *const _ as usize) : : "intel", "volatile"); + } } #[derive(Copy, Clone, Debug)] #[repr(packed)] pub struct IdtEntry { - pub offsetl: u16, - pub selector: u16, - pub zero: u8, - pub attribute: u8, - pub offsetm: u16, - pub offseth: u32, - pub zero2: u32 + offsetl: u16, + selector: u16, + zero: u8, + attribute: u8, + offsetm: u16, + offseth: u32, + zero2: u32 } impl IdtEntry { @@ -45,7 +62,12 @@ impl IdtEntry { } } - pub fn set_offset(&mut self, base: usize) { + pub fn set_flags(&mut self, flags: IdtFlags) { + self.attribute = flags.bits; + } + + pub fn set_offset(&mut self, selector: u16, base: usize) { + self.selector = selector; self.offsetl = base as u16; self.offsetm = (base >> 16) as u16; self.offseth = (base >> 32) as u32; diff --git a/src/arch/x86_64/io/io.rs b/src/arch/x86_64/io/io.rs new file mode 100644 index 0000000..a2d5fc6 --- /dev/null +++ b/src/arch/x86_64/io/io.rs @@ -0,0 +1,61 @@ +use core::cmp::PartialEq; +use core::ops::{BitAnd, BitOr, Not}; + +pub trait Io { + type Value: Copy + PartialEq + BitAnd + BitOr + Not; + + fn read(&self) -> Self::Value; + fn write(&mut self, value: Self::Value); + + fn readf(&self, flags: Self::Value) -> bool { + (self.read() & flags) as Self::Value == flags + } + + fn writef(&mut self, flags: Self::Value, value: bool) { + let tmp: Self::Value = match value { + true => self.read() | flags, + false => self.read() & !flags, + }; + self.write(tmp); + } +} + +pub struct ReadOnly { + inner: I +} + +impl ReadOnly { + pub fn new(inner: I) -> ReadOnly { + ReadOnly { + inner: inner + } + } + + pub fn read(&self) -> I::Value { + self.inner.read() + } + + pub fn readf(&self, flags: I::Value) -> bool { + self.inner.readf(flags) + } +} + +pub struct WriteOnly { + inner: I +} + +impl WriteOnly { + pub fn new(inner: I) -> WriteOnly { + WriteOnly { + inner: inner + } + } + + pub fn write(&mut self, value: I::Value) { + self.inner.write(value) + } + + pub fn writef(&mut self, flags: I::Value, value: bool) { + self.inner.writef(flags, value) + } +} diff --git a/src/arch/x86_64/io/mmio.rs b/src/arch/x86_64/io/mmio.rs new file mode 100644 index 0000000..1a1d199 --- /dev/null +++ b/src/arch/x86_64/io/mmio.rs @@ -0,0 +1,31 @@ +use core::intrinsics::{volatile_load, volatile_store}; +use core::mem::uninitialized; +use core::ops::{BitAnd, BitOr, Not}; + +use super::io::Io; + +#[repr(packed)] +pub struct Mmio { + value: T, +} + +impl Mmio { + /// Create a new Mmio without initializing + pub fn new() -> Self { + Mmio { + value: unsafe { uninitialized() } + } + } +} + +impl Io for Mmio where T: Copy + PartialEq + BitAnd + BitOr + Not { + type Value = T; + + fn read(&self) -> T { + unsafe { volatile_load(&self.value) } + } + + fn write(&mut self, value: T) { + unsafe { volatile_store(&mut self.value, value) }; + } +} diff --git a/src/arch/x86_64/io/mod.rs b/src/arch/x86_64/io/mod.rs new file mode 100644 index 0000000..86a1c34 --- /dev/null +++ b/src/arch/x86_64/io/mod.rs @@ -0,0 +1,9 @@ +/// I/O functions + +pub use self::io::*; +pub use self::mmio::*; +pub use self::pio::*; + +mod io; +mod mmio; +mod pio; diff --git a/src/arch/x86_64/io/pio.rs b/src/arch/x86_64/io/pio.rs new file mode 100644 index 0000000..1b10428 --- /dev/null +++ b/src/arch/x86_64/io/pio.rs @@ -0,0 +1,83 @@ +use core::marker::PhantomData; + +use super::io::Io; + +/// Generic PIO +#[derive(Copy, Clone)] +pub struct Pio { + port: u16, + value: PhantomData, +} + +impl Pio { + /// Create a PIO from a given port + pub fn new(port: u16) -> Self { + Pio:: { + port: port, + value: PhantomData, + } + } +} + +/// Read/Write for byte PIO +impl Io for Pio { + type Value = u8; + + /// Read + fn read(&self) -> u8 { + let value: u8; + unsafe { + asm!("in $0, $1" : "={al}"(value) : "{dx}"(self.port) : "memory" : "intel", "volatile"); + } + value + } + + /// Write + fn write(&mut self, value: u8) { + unsafe { + asm!("out $1, $0" : : "{al}"(value), "{dx}"(self.port) : "memory" : "intel", "volatile"); + } + } +} + +/// Read/Write for word PIO +impl Io for Pio { + type Value = u16; + + /// Read + fn read(&self) -> u16 { + let value: u16; + unsafe { + asm!("in $0, $1" : "={ax}"(value) : "{dx}"(self.port) : "memory" : "intel", "volatile"); + } + value + } + + /// Write + fn write(&mut self, value: u16) { + unsafe { + asm!("out $1, $0" : : "{ax}"(value), "{dx}"(self.port) : "memory" : "intel", "volatile"); + } + } +} + +/// Read/Write for doubleword PIO +impl Io for Pio { + type Value = u32; + + /// Read + fn read(&self) -> u32 { + let value: u32; + unsafe { + asm!("in $0, $1" : "={eax}"(value) : "{dx}"(self.port) : "memory" : "intel", "volatile"); + } + value + } + + /// Write + fn write(&mut self, value: u32) { + unsafe { + asm!("out $1, $0" : : "{eax}"(value), "{dx}"(self.port) : "memory" : "intel", "volatile"); + } + } +} diff --git a/src/arch/x86_64/main.rs b/src/arch/x86_64/main.rs index 4efbc67..50748b1 100644 --- a/src/arch/x86_64/main.rs +++ b/src/arch/x86_64/main.rs @@ -3,27 +3,69 @@ /// It must create the IDT with the correct entries, those entries are /// defined in other files inside of the `arch` module -use super::idt::{IDTR, IDT}; +use super::idt::{IDTR, IDT, IDT_PRESENT, IDT_RING_0, IDT_INTERRUPT}; +use super::mem::memset; + +extern { + /// The starting byte of the text (code) data segment. + static mut __text_start: u8; + /// The ending byte of the text (code) data segment. + static mut __text_end: u8; + /// The starting byte of the _.rodata_ (read-only data) segment. + static mut __rodata_start: u8; + /// The ending byte of the _.rodata_ (read-only data) segment. + static mut __rodata_end: u8; + /// The starting byte of the _.data_ segment. + static mut __data_start: u8; + /// The ending byte of the _.data_ segment. + static mut __data_end: u8; + /// The starting byte of the _.bss_ (uninitialized data) segment. + static mut __bss_start: u8; + /// The ending byte of the _.bss_ (uninitialized data) segment. + static mut __bss_end: u8; +} + +/// Test of zero values in BSS. +static BSS_TEST_ZERO: usize = 0; +/// Test of non-zero values in BSS. +static BSS_TEST_NONZERO: usize = 0xFFFFFFFFFFFFFFFF; -#[naked] #[no_mangle] pub unsafe extern "C" fn kmain() { asm!("xchg bx, bx" : : : : "intel", "volatile"); + // Zero BSS, this initializes statics that are set to 0 + { + let start_ptr = &mut __bss_start as *mut u8; + let end_ptr = & __bss_end as *const u8 as usize; + + if start_ptr as usize <= end_ptr { + let size = end_ptr - start_ptr as usize; + memset(start_ptr, 0, size); + } + + //debug_assert_eq!(BSS_TEST_ZERO, 0); + //debug_assert_eq!(BSS_TEST_NONZERO, 0xFFFFFFFFFFFFFFFF); + } + + asm!("xchg bx, bx" : : : : "intel", "volatile"); + + //Set up IDT for entry in IDT.iter_mut() { - entry.attribute = 1 << 7 | 0xE; - entry.selector = 8; - entry.set_offset(blank as usize); - entry.zero = 0; - entry.zero2 = 0; + entry.set_flags(IDT_PRESENT | IDT_RING_0 | IDT_INTERRUPT); + entry.set_offset(8, blank as usize); } IDTR.set_slice(&IDT); - asm!("lidt [rax]" : : "{rax}"(&IDTR as *const _ as usize) : : "intel", "volatile"); + IDTR.load(); asm!("xchg bx, bx" : : : : "intel", "volatile"); asm!("int 0xFF" : : : : "intel", "volatile"); + asm!("xchg bx, bx" : : : : "intel", "volatile"); + + print!("TEST\n"); + loop{ asm!("hlt" : : : : "intel", "volatile"); } diff --git a/src/arch/x86_64/mem.rs b/src/arch/x86_64/mem.rs new file mode 100644 index 0000000..3b87427 --- /dev/null +++ b/src/arch/x86_64/mem.rs @@ -0,0 +1,70 @@ +/// Memcpy +/// +/// Copy N bytes of memory from one location to another. +#[no_mangle] +pub unsafe extern fn memcpy(dest: *mut u8, src: *const u8, + n: usize) -> *mut u8 { + let mut i = 0; + while i < n { + *dest.offset(i as isize) = *src.offset(i as isize); + i += 1; + } + + dest +} + +/// Memmove +/// +/// Copy N bytes of memory from src to dest. The memory areas may overlap. +#[no_mangle] +pub unsafe extern fn memmove(dest: *mut u8, src: *const u8, + n: usize) -> *mut u8 { + if src < dest as *const u8 { + let mut i = n; + while i != 0 { + i -= 1; + *dest.offset(i as isize) = *src.offset(i as isize); + } + } else { + let mut i = 0; + while i < n { + *dest.offset(i as isize) = *src.offset(i as isize); + i += 1; + } + } + + dest +} + +/// Memset +/// +/// Fill a block of memory with a specified value. +#[no_mangle] +pub unsafe extern fn memset(s: *mut u8, c: i32, n: usize) -> *mut u8 { + let mut i = 0; + while i < n { + *s.offset(i as isize) = c as u8; + i += 1; + } + + s +} + +/// Memcmp +/// +/// Compare two blocks of memory. +#[no_mangle] +pub unsafe extern fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 { + let mut i = 0; + + while i < n { + let a = *s1.offset(i as isize); + let b = *s2.offset(i as isize); + if a != b { + return a as i32 - b as i32 + } + i += 1; + } + + 0 +} diff --git a/src/arch/x86_64/mod.rs b/src/arch/x86_64/mod.rs index 774b90f..d78402d 100644 --- a/src/arch/x86_64/mod.rs +++ b/src/arch/x86_64/mod.rs @@ -4,8 +4,20 @@ pub mod gdt; /// Interrupt descriptor table pub mod idt; +/// IO Handling +pub mod io; + /// IRQ Handling pub mod irq; /// Initialization and main function pub mod main; + +/// Memcpy, memmove, etc. +pub mod mem; + +/// Serial driver and print! support +pub mod serial; + +/// Task state segment +pub mod tss; diff --git a/src/arch/x86_64/serial.rs b/src/arch/x86_64/serial.rs new file mode 100644 index 0000000..dcc9abf --- /dev/null +++ b/src/arch/x86_64/serial.rs @@ -0,0 +1,39 @@ +use core::fmt; +use super::io::{Io, Pio}; + +pub struct SerialConsole { + status: Pio, + data: Pio +} + +impl SerialConsole { + pub fn new() -> SerialConsole { + SerialConsole { + status: Pio::new(0x3F8 + 5), + data: Pio::new(0x3F8) + } + } + + pub fn write(&mut self, bytes: &[u8]) { + for byte in bytes.iter() { + while !self.status.readf(0x20) {} + self.data.write(*byte); + + if *byte == 8 { + while !self.status.readf(0x20) {} + self.data.write(0x20); + + while !self.status.readf(0x20) {} + self.data.write(8); + } + } + } +} + +impl fmt::Write for SerialConsole { + fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { + self.write(s.as_bytes()); + + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index b28bbe5..23c0a18 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,10 +66,28 @@ #![feature(asm)] #![feature(const_fn)] +#![feature(core_intrinsics)] #![feature(lang_items)] #![feature(naked_functions)] #![no_std] +#[macro_use] +extern crate bitflags; + +/// Print to console +macro_rules! print { + ($($arg:tt)*) => ({ + use $crate::core::fmt::Write; + let _ = write!($crate::arch::serial::SerialConsole::new(), $($arg)*); + }); +} + +/// Print with new line to console +macro_rules! println { + ($fmt:expr) => (print!(concat!($fmt, "\n"))); + ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); +} + /// Architecture specific items pub mod arch; @@ -81,3 +99,10 @@ extern "C" fn eh_personality() {} /// Required to handle panics #[lang = "panic_fmt"] extern "C" fn panic_fmt() -> ! {loop{}} + +#[allow(non_snake_case)] +#[no_mangle] +/// Required to handle panics +pub extern "C" fn _Unwind_Resume() -> ! { + loop {} +}