From b334e5bc31e5e2da9f56be531142e83d27d9007f Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Wed, 2 Nov 2022 13:30:20 +0100 Subject: [PATCH] kernel: Split debug shell and uart driver. I've added a `platform` module to store all platform specific code, (not architecture specific) as some platforms will require custom drivers, e.g. for debug output and for the random number generation code. The platform code is in charge of mapping any peripherals needed to work, also to initialize any other parts of the kernel that require drivers, for example the debug shell. Signed-off-by: Jean-Pierre De Jesus DIAZ --- kernel/src/debug/macros.rs | 53 +++++ kernel/src/debug/mod.rs | 315 +------------------------- kernel/src/debug/shell.rs | 230 +++++++++++++++++++ kernel/src/io.rs | 14 ++ kernel/src/main.rs | 43 +--- kernel/src/platform/mod.rs | 12 + kernel/src/platform/precursor/mod.rs | 11 + kernel/src/platform/precursor/uart.rs | 103 +++++++++ 8 files changed, 431 insertions(+), 350 deletions(-) create mode 100644 kernel/src/debug/macros.rs create mode 100644 kernel/src/debug/shell.rs create mode 100644 kernel/src/io.rs create mode 100644 kernel/src/platform/mod.rs create mode 100644 kernel/src/platform/precursor/mod.rs create mode 100644 kernel/src/platform/precursor/uart.rs diff --git a/kernel/src/debug/macros.rs b/kernel/src/debug/macros.rs new file mode 100644 index 000000000..f05f19877 --- /dev/null +++ b/kernel/src/debug/macros.rs @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2020 Sean Cross +// SPDX-FileCopyrightText: 2022 Foundation Devices, Inc. +// SPDX-License-Identifier: Apache-2.0 + +/// Prints to the debug output directly. +#[cfg(baremetal)] +#[macro_export] +macro_rules! print { + ($($args:tt)+) => {{ + #[allow(unused_unsafe)] + unsafe { + use core::fmt::Write; + if let Some(stream) = crate::debug::shell::OUTPUT.as_mut() { + write!(stream, $($args)+).unwrap(); + } + } + }}; +} + +/// Prints to the debug output directly, with a newline. +#[cfg(baremetal)] +#[macro_export] +macro_rules! println { + () => ({ + print!("\r\n") + }); + ($fmt:expr) => ({ + print!(concat!($fmt, "\r\n")) + }); + ($fmt:expr, $($args:tt)+) => ({ + print!(concat!($fmt, "\r\n"), $($args)+) + }); +} + +#[cfg(feature = "debug-print")] +#[macro_export] +macro_rules! klog { + () => ({ + print!(" [{}:{}]", file!(), line!()) + }); + ($fmt:expr) => ({ + print!(concat!(" [{}:{} ", $fmt, "]"), file!(), line!()) + }); + ($fmt:expr, $($args:tt)+) => ({ + print!(concat!(" [{}:{} ", $fmt, "]"), file!(), line!(), $($args)+) + }); +} + +#[cfg(not(feature = "debug-print"))] +#[macro_export] +macro_rules! klog { + ($($args:tt)+) => {{}}; +} diff --git a/kernel/src/debug/mod.rs b/kernel/src/debug/mod.rs index 5204d6fef..04452ee3e 100644 --- a/kernel/src/debug/mod.rs +++ b/kernel/src/debug/mod.rs @@ -1,318 +1,7 @@ // SPDX-FileCopyrightText: 2020 Sean Cross // SPDX-License-Identifier: Apache-2.0 -#[cfg(baremetal)] -use core::fmt::{Error, Write}; -#[cfg(baremetal)] -use utralib::generated::*; - -#[cfg(baremetal)] -pub static mut DEBUG_OUTPUT: Option<&'static mut dyn Write> = None; - -pub use crate::arch::process::Process as ArchProcess; - #[macro_use] -#[cfg(all( - not(test), - baremetal, - any(feature = "debug-print", feature = "print-panics") -))] -pub mod debug_print_hardware { - // the HW device mapping is done in main.rs/init(); the virtual address has to be in the top 4MiB as it is the only page shared among all processes - pub const SUPERVISOR_UART_ADDR: *mut usize = 0xffcf_0000 as *mut usize; // see https://github.com/betrusted-io/xous-core/blob/master/docs/memory.md -} -#[cfg(all( - not(test), - baremetal, - any(feature = "debug-print", feature = "print-panics") -))] -pub use crate::debug::debug_print_hardware::SUPERVISOR_UART_ADDR; - -#[cfg(baremetal)] -#[macro_export] -macro_rules! print { - ($($args:tt)+) => {{ - #[allow(unused_unsafe)] - unsafe { - if let Some(mut stream) = crate::debug::DEBUG_OUTPUT.as_mut() { - write!(&mut stream, $($args)+).unwrap(); - } - } - }}; -} - -#[cfg(baremetal)] -#[macro_export] -macro_rules! println -{ - () => ({ - print!("\r\n") - }); - ($fmt:expr) => ({ - print!(concat!($fmt, "\r\n")) - }); - ($fmt:expr, $($args:tt)+) => ({ - print!(concat!($fmt, "\r\n"), $($args)+) - }); -} - -#[cfg(baremetal)] -pub struct Uart {} +mod macros; #[cfg(baremetal)] -pub static mut UART: Uart = Uart {}; - -#[cfg(all(baremetal, feature = "wrap-print"))] -static mut CHAR_COUNT: usize = 0; - -#[cfg(baremetal)] -impl Uart { - #[allow(dead_code)] - pub fn init(self) { - unsafe { DEBUG_OUTPUT = Some(&mut UART) }; - let mut uart_csr = CSR::new(crate::debug::SUPERVISOR_UART_ADDR as *mut u32); - uart_csr.rmwf(utra::uart::EV_ENABLE_RX, 1); - } - - pub fn putc(&self, c: u8) { - if unsafe { DEBUG_OUTPUT.is_none() } { - return; - } - - let mut uart_csr = CSR::new(crate::debug::SUPERVISOR_UART_ADDR as *mut u32); - // Wait until TXFULL is `0` - while uart_csr.r(utra::uart::TXFULL) != 0 {} - #[cfg(feature = "wrap-print")] - unsafe { - if c == b'\n' { - CHAR_COUNT = 0; - } else if CHAR_COUNT > 80 { - CHAR_COUNT = 0; - self.putc(b'\n'); - self.putc(b'\r'); - self.putc(b' '); - self.putc(b' '); - self.putc(b' '); - self.putc(b' '); - } else { - CHAR_COUNT += 1; - } - } - uart_csr.wfo(utra::uart::RXTX_RXTX, c as u32); - } - - #[allow(dead_code)] - pub fn getc(&self) -> Option { - if unsafe { DEBUG_OUTPUT.is_none() } { - return None; - } - let mut uart_csr = CSR::new(crate::debug::SUPERVISOR_UART_ADDR as *mut u32); - // If EV_PENDING_RX is 1, return the pending character. - // Otherwise, return None. - match uart_csr.rf(utra::uart::EV_PENDING_RX) { - 0 => None, - _ => { - let ret = Some(uart_csr.r(utra::uart::RXTX) as u8); - uart_csr.wfo(utra::uart::EV_PENDING_RX, 1); - ret - } - } - } -} - -#[cfg(all( - baremetal, - not(test), - any(feature = "debug-print", feature = "print-panics") -))] -pub fn irq(_irq_number: usize, _arg: *mut usize) { - let uart = Uart {}; - while let Some(b) = uart.getc() { - process_irq_character(b); - } - // uart.acknowledge_irq(); -} - -#[cfg(all( - baremetal, - not(test), - any(feature = "debug-print", feature = "print-panics") -))] -fn process_irq_character(b: u8) { - match b { - b'i' => { - println!("Interrupt handlers:"); - println!(" IRQ | Process | Handler | Argument"); - crate::services::SystemServices::with(|system_services| { - crate::irq::for_each_irq(|irq, pid, address, arg| { - println!( - " {}: {} @ {:x?} {:x?}", - irq, - system_services.process_name(*pid).unwrap_or(""), - address, - arg - ); - }); - }); - } - b'm' => { - println!("Printing memory page tables"); - crate::services::SystemServices::with(|system_services| { - let current_pid = system_services.current_pid(); - for process in &system_services.processes { - if !process.free() { - println!( - "PID {} {}:", - process.pid, - system_services.process_name(process.pid).unwrap_or("") - ); - process.activate().unwrap(); - crate::arch::mem::MemoryMapping::current().print_map(); - println!(); - } - } - system_services - .get_process(current_pid) - .unwrap() - .activate() - .unwrap(); - }); - } - b'p' => { - println!("Printing processes"); - crate::services::SystemServices::with(|system_services| { - let current_pid = system_services.current_pid(); - for process in &system_services.processes { - if !process.free() { - process.activate().unwrap(); - let mut connection_count = 0; - ArchProcess::with_inner(|process_inner| { - for conn in &process_inner.connection_map { - if conn.is_some() { - connection_count += 1; - } - } - }); - println!( - "{:?} conns:{}/32 {}", - process, - connection_count, - system_services.process_name(process.pid).unwrap_or("") - ); - } - } - system_services - .get_process(current_pid) - .unwrap() - .activate() - .unwrap(); - }); - } - b'P' => { - println!("Printing processes and threads"); - crate::services::SystemServices::with(|system_services| { - let current_pid = system_services.current_pid(); - for process in &system_services.processes { - if !process.free() { - println!( - "{:?} {}:", - process, - system_services.process_name(process.pid).unwrap_or("") - ); - process.activate().unwrap(); - crate::arch::process::Process::with_current_mut(|arch_process| { - arch_process.print_all_threads() - }); - println!(); - } - } - system_services - .get_process(current_pid) - .unwrap() - .activate() - .unwrap(); - }); - } - b'r' => { - println!("RAM usage:"); - let mut total_bytes = 0; - crate::services::SystemServices::with(|system_services| { - crate::mem::MemoryManager::with(|mm| { - for process in &system_services.processes { - if !process.free() { - let bytes_used = mm.ram_used_by(process.pid); - total_bytes += bytes_used; - println!( - " PID {:>3}: {:>4} k {}", - process.pid, - bytes_used / 1024, - system_services.process_name(process.pid).unwrap_or("") - ); - } - } - }); - }); - println!("{} k total", total_bytes / 1024); - } - b's' => { - println!("Servers in use:"); - crate::services::SystemServices::with(|system_services| { - println!(" idx | pid | process | sid"); - println!(" --- + --- + -------------------- | ------------------"); - for (idx, server) in system_services.servers.iter().enumerate() { - if let Some(s) = server { - println!( - " {:3} | {:3} | {:20} | {:x?}", - idx, - s.pid, - system_services.process_name(s.pid).unwrap_or(""), - s.sid - ); - } - } - }); - } - b'h' => { - println!("Xous Kernel Debug"); - println!("key | command"); - println!("--- + -----------------------"); - println!(" i | print irq handlers"); - println!(" m | print MMU page tables of all processes"); - println!(" p | print all processes"); - println!(" P | print all processes and threads"); - println!(" r | report RAM usage of all processes"); - println!(" s | print all allocated servers"); - } - _ => {} - } -} - -#[cfg(baremetal)] -impl Write for Uart { - fn write_str(&mut self, s: &str) -> Result<(), Error> { - for c in s.bytes() { - self.putc(c); - } - Ok(()) - } -} - -#[cfg(feature = "debug-print")] -#[macro_export] -macro_rules! klog -{ - () => ({ - print!(" [{}:{}]", file!(), line!()) - }); - ($fmt:expr) => ({ - print!(concat!(" [{}:{} ", $fmt, "]"), file!(), line!()) - }); - ($fmt:expr, $($args:tt)+) => ({ - print!(concat!(" [{}:{} ", $fmt, "]"), file!(), line!(), $($args)+) - }); -} - -#[cfg(not(feature = "debug-print"))] -#[macro_export] -macro_rules! klog { - ($($args:tt)+) => {{}}; -} +pub mod shell; diff --git a/kernel/src/debug/shell.rs b/kernel/src/debug/shell.rs new file mode 100644 index 000000000..86408ad97 --- /dev/null +++ b/kernel/src/debug/shell.rs @@ -0,0 +1,230 @@ +// SPDX-FileCopyrightText: 2020 Sean Cross +// SPDX-FileCopyrightText: 2022 Foundation Devices, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use core::fmt; +use crate::{ + args::KernelArguments, + io::{SerialWrite, SerialRead}, +}; + +/// Instance of the shell output. +pub static mut OUTPUT: Option = None; + +/// Shell output. +pub struct Output { + serial: &'static mut dyn SerialWrite, + #[cfg(feature = "wrap-print")] + character_count: usize, +} + +impl Output { + fn new(serial: &'static mut dyn SerialWrite) -> Output { + Output { + serial, + #[cfg(feature = "wrap-print")] + character_count: 0, + } + } +} + +impl fmt::Write for Output { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.bytes() { + #[cfg(feature = "wrap-print")] + if c == b'\n' { + self.character_count = 0; + } else if self.character_count > 80 { + self.character_count = 0; + self.uart.putc(b'\n'); + self.uart.putc(b'\r'); + self.uart.putc(b' '); + self.uart.putc(b' '); + self.uart.putc(b' '); + self.uart.putc(b' '); + } else { + self.character_count += 1; + } + + self.serial.putc(c); + } + Ok(()) + } +} + +/// Initialize the kernel shell. +/// +/// This should be called in platform initialization code. +pub fn init(serial: &'static mut dyn SerialWrite) { + unsafe { + OUTPUT = Some(Output::new(serial)) + } + + // Print the processed kernel arguments + let args = KernelArguments::get(); + println!("Kernel arguments:"); + for arg in args.iter() { + println!(" {}", arg); + } +} + +/// Process possible characters received through a serial interface. +/// +/// This should be called when a serial interface has new data, for example, +/// on an interrupt. +pub fn process_characters(serial: &mut R) { + while let Some(b) = serial.getc() { + handle_character(b); + } +} + +fn handle_character(b: u8) { + use crate::services::ArchProcess; + + match b { + b'i' => { + println!("Interrupt handlers:"); + println!(" IRQ | Process | Handler | Argument"); + crate::services::SystemServices::with(|system_services| { + crate::irq::for_each_irq(|irq, pid, address, arg| { + println!( + " {}: {} @ {:x?} {:x?}", + irq, + system_services.process_name(*pid).unwrap_or(""), + address, + arg + ); + }); + }); + } + b'm' => { + println!("Printing memory page tables"); + crate::services::SystemServices::with(|system_services| { + let current_pid = system_services.current_pid(); + for process in &system_services.processes { + if !process.free() { + println!( + "PID {} {}:", + process.pid, + system_services.process_name(process.pid).unwrap_or("") + ); + process.activate().unwrap(); + crate::arch::mem::MemoryMapping::current().print_map(); + println!(); + } + } + system_services + .get_process(current_pid) + .unwrap() + .activate() + .unwrap(); + }); + } + b'p' => { + println!("Printing processes"); + crate::services::SystemServices::with(|system_services| { + let current_pid = system_services.current_pid(); + for process in &system_services.processes { + if !process.free() { + process.activate().unwrap(); + let mut connection_count = 0; + ArchProcess::with_inner(|process_inner| { + for conn in &process_inner.connection_map { + if conn.is_some() { + connection_count += 1; + } + } + }); + println!( + "{:?} conns:{}/32 {}", + process, + connection_count, + system_services.process_name(process.pid).unwrap_or("") + ); + } + } + system_services + .get_process(current_pid) + .unwrap() + .activate() + .unwrap(); + }); + } + b'P' => { + println!("Printing processes and threads"); + crate::services::SystemServices::with(|system_services| { + let current_pid = system_services.current_pid(); + for process in &system_services.processes { + if !process.free() { + println!( + "{:?} {}:", + process, + system_services.process_name(process.pid).unwrap_or("") + ); + process.activate().unwrap(); + crate::arch::process::Process::with_current_mut(|arch_process| { + arch_process.print_all_threads() + }); + println!(); + } + } + system_services + .get_process(current_pid) + .unwrap() + .activate() + .unwrap(); + }); + } + b'r' => { + println!("RAM usage:"); + let mut total_bytes = 0; + crate::services::SystemServices::with(|system_services| { + crate::mem::MemoryManager::with(|mm| { + for process in &system_services.processes { + if !process.free() { + let bytes_used = mm.ram_used_by(process.pid); + total_bytes += bytes_used; + println!( + " PID {:>3}: {:>4} k {}", + process.pid, + bytes_used / 1024, + system_services.process_name(process.pid).unwrap_or("") + ); + } + } + }); + }); + println!("{} k total", total_bytes / 1024); + } + b's' => { + println!("Servers in use:"); + crate::services::SystemServices::with(|system_services| { + println!(" idx | pid | process | sid"); + println!(" --- + --- + -------------------- | ------------------"); + for (idx, server) in system_services.servers.iter().enumerate() { + if let Some(s) = server { + println!( + " {:3} | {:3} | {:20} | {:x?}", + idx, + s.pid, + system_services.process_name(s.pid).unwrap_or(""), + s.sid + ); + } + } + }); + } + b'h' => { + println!("Xous Kernel Debug"); + println!("key | command"); + println!("--- + -----------------------"); + println!(" i | print irq handlers"); + println!(" m | print MMU page tables of all processes"); + println!(" p | print all processes"); + println!(" P | print all processes and threads"); + println!(" r | report RAM usage of all processes"); + println!(" s | print all allocated servers"); + } + _ => {} + } +} diff --git a/kernel/src/io.rs b/kernel/src/io.rs new file mode 100644 index 000000000..9caa56b9e --- /dev/null +++ b/kernel/src/io.rs @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2022 Foundation Devices, Inc. +// SPDX-License-Identifier: Apache-2.0 + +/// A trait for serial like drivers which are byte-oriented sinks. +pub trait SerialWrite { + /// Write a single byte. + fn putc(&mut self, b: u8); +} + +/// A trait for serial like drivers which allows reading from a source. +pub trait SerialRead { + /// Read a single byte. + fn getc(&mut self) -> Option; +} diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 60db7711d..0cb7c3ef0 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -18,9 +18,11 @@ mod arch; #[macro_use] mod args; +mod io; mod irq; mod macros; mod mem; +mod platform; mod server; mod services; mod syscall; @@ -59,45 +61,12 @@ pub unsafe extern "C" fn init(arg_offset: *const u32, init_offset: *const u32, r system_services.init_from_memory(init_offset, &args) }); - // Now that the memory manager is set up, perform any arch-specific initializations. + // Now that the memory manager is set up, perform any architecture and + // platform specific initializations. arch::init(); + platform::init(); - // Either map memory using a syscall, or if we're debugging the syscall - // handler then directly map it. - #[cfg(any(feature = "debug-print", feature = "print-panics"))] - { - use utralib::generated::*; - xous_kernel::claim_interrupt( - utra::uart::UART_IRQ, - debug::irq, - core::ptr::null_mut::(), - ) - .expect("Couldn't claim debug interrupt"); - // Map the serial port so println!() works as expected. - mem::MemoryManager::with_mut(|memory_manager| { - memory_manager - .map_range( - utra::uart::HW_UART_BASE as *mut u8, - ((debug::SUPERVISOR_UART_ADDR as u32) & !4095) as *mut u8, - 4096, - PID::new(1).unwrap(), - MemoryFlags::R | MemoryFlags::W, - MemoryType::Default, - ) - .expect("unable to map serial port") - }); - debug::Uart {}.init(); - println!("KMAIN (clean boot): Supervisor mode started..."); - println!("Claiming IRQ {} via syscall...", utra::uart::UART_IRQ); - print!("}} "); - - // Print the processed kernel arguments - let args = args::KernelArguments::get(); - println!("Kernel arguments:"); - for arg in args.iter() { - println!(" {}", arg); - } - } + println!("KMAIN (clean boot): Supervisor mode started..."); // rand::init() already clears the initial pipe, but pump the TRNG a little more out of no other reason than sheer paranoia arch::rand::get_u32(); diff --git a/kernel/src/platform/mod.rs b/kernel/src/platform/mod.rs new file mode 100644 index 000000000..66e742e3d --- /dev/null +++ b/kernel/src/platform/mod.rs @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2022 Foundation Devices, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[cfg(any(feature="precursor", feature="renode"))] +pub mod precursor; + +/// Platform specific initialization. +#[cfg(not(any(unix, windows)))] +pub fn init() { + #[cfg(any(feature="precursor", feature="renode"))] + self::precursor::init(); +} \ No newline at end of file diff --git a/kernel/src/platform/precursor/mod.rs b/kernel/src/platform/precursor/mod.rs new file mode 100644 index 000000000..f02ec11ff --- /dev/null +++ b/kernel/src/platform/precursor/mod.rs @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2022 Foundation Devices, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[cfg(any(feature = "debug-print", feature = "print-panics"))] +pub mod uart; + +/// Precursor specific initialization. +pub fn init() { + #[cfg(any(feature = "debug-print", feature = "print-panics"))] + self::uart::init(); +} \ No newline at end of file diff --git a/kernel/src/platform/precursor/uart.rs b/kernel/src/platform/precursor/uart.rs new file mode 100644 index 000000000..0001bbddb --- /dev/null +++ b/kernel/src/platform/precursor/uart.rs @@ -0,0 +1,103 @@ +// SPDX-FileCopyrightText: 2020 Sean Cross +// SPDX-FileCopyrightText: 2022 Foundation Devices, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + io::{SerialWrite, SerialRead}, + mem::MemoryManager, + debug::shell::process_characters, + PID, +}; +use utralib::generated::*; +use xous_kernel::{MemoryFlags, MemoryType}; + +/// UART virtual address. +/// +/// See https://github.com/betrusted-io/xous-core/blob/master/docs/memory.md +pub const UART_ADDR: usize = 0xffcf_0000; + +/// UART instance. +/// +/// Initialized by [`init`]. +pub static mut UART: Option = None; + +/// UART peripheral driver. +pub struct Uart { + uart_csr: CSR, + callback: fn(&mut Self), +} + +impl Uart { + pub fn new(addr: usize, callback: fn(&mut Self)) -> Uart { + Uart { + uart_csr: CSR::new(addr as *mut u32), + callback, + } + } + + pub fn init(&mut self) { + self.uart_csr.rmwf(utra::uart::EV_ENABLE_RX, 1); + } + + pub fn irq(_irq_number: usize, arg: *mut usize) { + let uart = unsafe { &mut *(arg as *mut Uart) }; + (uart.callback)(uart); + // uart.acknowledge_irq(); + } +} + +impl SerialWrite for Uart { + fn putc(&mut self, c: u8) { + // Wait until TXFULL is `0` + while self.uart_csr.r(utra::uart::TXFULL) != 0 {} + self.uart_csr.wfo(utra::uart::RXTX_RXTX, c as u32); + } +} + +impl SerialRead for Uart { + fn getc(&mut self) -> Option { + // If EV_PENDING_RX is 1, return the pending character. + // Otherwise, return None. + match self.uart_csr.rf(utra::uart::EV_PENDING_RX) { + 0 => None, + _ => { + let ret = Some(self.uart_csr.r(utra::uart::RXTX) as u8); + self.uart_csr.wfo(utra::uart::EV_PENDING_RX, 1); + ret + } + } + } +} + +/// Initialize UART driver and debug shell. +pub fn init() { + // Map the UART peripheral. + MemoryManager::with_mut(|memory_manager| { + memory_manager + .map_range( + utra::uart::HW_UART_BASE as *mut u8, + (UART_ADDR & !4095) as *mut u8, + 4096, + PID::new(1).unwrap(), + MemoryFlags::R | MemoryFlags::W, + MemoryType::Default, + ) + .expect("unable to map serial port") + }); + + unsafe { + let mut uart = Uart::new(UART_ADDR, process_characters); + uart.init(); + + UART = Some(uart); + crate::debug::shell::init(UART.as_mut().unwrap()); + + // Claim UART interrupt. + println!("Claiming IRQ {} via syscall...", utra::uart::UART_IRQ); + xous_kernel::claim_interrupt( + utra::uart::UART_IRQ, + Uart::irq, + (UART.as_mut().unwrap() as *mut Uart) as *mut usize, + ).expect("Couldn't claim debug interrupt"); + } +}