refactor: change add_char_device
This commit is contained in:
+9
-4
@@ -9,7 +9,12 @@ extern crate libusr;
|
||||
fn main() -> i32 {
|
||||
let mut buf = [0; 128];
|
||||
|
||||
print!("\x1B[2J\x1B[1;1H");
|
||||
println!("Hello!");
|
||||
|
||||
loop {
|
||||
print!("> ");
|
||||
|
||||
let count = unsafe {
|
||||
libusr::sys::sys_read(0, buf.as_mut_ptr(), buf.len())
|
||||
};
|
||||
@@ -21,13 +26,13 @@ fn main() -> i32 {
|
||||
|
||||
if let Ok(s) = core::str::from_utf8(&buf[..count]) {
|
||||
println!("Got string {:?}", s);
|
||||
|
||||
if s == "quit" {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
println!("Got string (non-utf8) {:?}", &buf[..count]);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
libusr::sys::sys_ex_nanosleep(1_000_000_000, core::ptr::null_mut());
|
||||
}
|
||||
}
|
||||
-1
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ use crate::arch::{
|
||||
machine,
|
||||
};
|
||||
use crate::fs::devfs;
|
||||
use crate::dev::{fdt::DeviceTree, irq::IntSource, Device};
|
||||
use crate::dev::{fdt::{DeviceTree, find_prop}, irq::IntSource, Device};
|
||||
use error::Errno;
|
||||
use crate::config::{CONFIG, ConfigKey};
|
||||
//use crate::debug::Level;
|
||||
use crate::mem::{
|
||||
self, heap,
|
||||
@@ -17,6 +19,39 @@ use cortex_a::asm::barrier::{self, dsb, isb};
|
||||
use cortex_a::registers::{SCTLR_EL1, VBAR_EL1};
|
||||
use tock_registers::interfaces::{ReadWriteable, Writeable};
|
||||
|
||||
fn init_device_tree(fdt_base_phys: usize) -> Result<(), Errno> {
|
||||
use fdt_rs::prelude::*;
|
||||
|
||||
let fdt = if fdt_base_phys != 0 {
|
||||
DeviceTree::from_phys(fdt_base_phys + 0xFFFFFF8000000000)?
|
||||
} else {
|
||||
warnln!("No FDT present");
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
use crate::debug::Level;
|
||||
fdt.dump(Level::Debug);
|
||||
|
||||
let mut cfg = CONFIG.lock();
|
||||
|
||||
if let Some(chosen) = fdt.node_by_path("/chosen") {
|
||||
if let Some(initrd_start) = find_prop(chosen.clone(), "linux,initrd-start") {
|
||||
let initrd_end = find_prop(chosen.clone(), "linux,initrd-end").unwrap();
|
||||
let initrd_start = initrd_start.u32(0).unwrap() as usize;
|
||||
let initrd_end = initrd_end.u32(0).unwrap() as usize;
|
||||
|
||||
cfg.set_usize(ConfigKey::InitrdBase, initrd_start);
|
||||
cfg.set_usize(ConfigKey::InitrdSize, initrd_end - initrd_start);
|
||||
}
|
||||
|
||||
if let Some(cmdline) = find_prop(chosen, "bootargs") {
|
||||
cfg.set_cmdline(cmdline.str().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn __aa64_bsp_main(fdt_base: usize) -> ! {
|
||||
// Disable FP instruction trapping
|
||||
@@ -49,6 +84,8 @@ extern "C" fn __aa64_bsp_main(fdt_base: usize) -> ! {
|
||||
// physical memory
|
||||
machine::init_board_early().unwrap();
|
||||
|
||||
init_device_tree(fdt_base).expect("Device tree init failed");
|
||||
|
||||
// Setup a heap
|
||||
unsafe {
|
||||
let heap_base_phys = phys::alloc_contiguous_pages(PageUsage::KernelHeap, 4096)
|
||||
@@ -61,29 +98,14 @@ extern "C" fn __aa64_bsp_main(fdt_base: usize) -> ! {
|
||||
|
||||
machine::init_board().unwrap();
|
||||
|
||||
let initrd;
|
||||
if fdt_base != 0 {
|
||||
let fdt = DeviceTree::from_phys(fdt_base + 0xFFFFFF8000000000);
|
||||
if let Ok(fdt) = fdt {
|
||||
use crate::debug::Level;
|
||||
fdt.dump(Level::Debug);
|
||||
initrd = fdt.initrd();
|
||||
} else {
|
||||
initrd = None;
|
||||
errorln!("Failed to init FDT");
|
||||
}
|
||||
} else {
|
||||
initrd = None;
|
||||
warnln!("No FDT present");
|
||||
}
|
||||
|
||||
debugln!("Config: {:#x?}", CONFIG.lock());
|
||||
infoln!("Machine init finished");
|
||||
|
||||
unsafe {
|
||||
machine::local_timer().enable().unwrap();
|
||||
machine::local_timer().init_irqs().unwrap();
|
||||
|
||||
proc::enter(initrd);
|
||||
proc::enter();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ use crate::dev::{
|
||||
timer::TimestampSource,
|
||||
Device,
|
||||
};
|
||||
use crate::fs::devfs::{self, CharDeviceType};
|
||||
use crate::mem::phys;
|
||||
use error::Errno;
|
||||
|
||||
@@ -43,6 +44,7 @@ pub fn init_board() -> Result<(), Errno> {
|
||||
GPIO.enable()?;
|
||||
|
||||
UART0.init_irqs()?;
|
||||
devfs::add_char_device(&UART0, CharDeviceType::TtySerial)?;
|
||||
|
||||
R_WDOG.enable()?;
|
||||
|
||||
|
||||
@@ -71,7 +71,6 @@ impl RtcDevice for Rtc {}
|
||||
impl IntSource for Rtc {
|
||||
fn handle_irq(&self) -> Result<(), Errno> {
|
||||
self.regs.get().lock().arm_alarm0_irq(1);
|
||||
debugln!("Tick!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@ use crate::arch::machine::{self, IrqNumber};
|
||||
use crate::dev::{
|
||||
irq::{IntController, IntSource},
|
||||
serial::SerialDevice,
|
||||
Device,
|
||||
tty::CharRing, Device,
|
||||
};
|
||||
use vfs::CharDevice;
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
@@ -78,6 +79,7 @@ struct UartInner {
|
||||
|
||||
pub(super) struct Uart {
|
||||
inner: InitOnce<IrqSafeSpinLock<UartInner>>,
|
||||
ring: CharRing<16>,
|
||||
base: usize,
|
||||
irq: IrqNumber,
|
||||
}
|
||||
@@ -120,6 +122,23 @@ impl SerialDevice for Uart {
|
||||
}
|
||||
}
|
||||
|
||||
impl CharDevice for Uart {
|
||||
fn read(&self, blocking: bool, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
assert!(blocking);
|
||||
self.ring.line_read(data, self)
|
||||
}
|
||||
|
||||
fn write(&self, blocking: bool, data: &[u8]) -> Result<usize, Errno> {
|
||||
assert!(blocking);
|
||||
for &byte in data {
|
||||
unsafe {
|
||||
self.send(byte)?;
|
||||
}
|
||||
}
|
||||
Ok(data.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntSource for Uart {
|
||||
fn handle_irq(&self) -> Result<(), Errno> {
|
||||
let byte = self.inner.get().lock().regs.DR_DLL.get();
|
||||
@@ -132,8 +151,7 @@ impl IntSource for Uart {
|
||||
}
|
||||
}
|
||||
|
||||
use crate::dev::gpio::GpioDevice;
|
||||
machine::GPIO.toggle_pin(machine::PinAddress::new(3, 26));
|
||||
self.ring.putc(byte as u8, false).ok();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -150,6 +168,7 @@ impl Uart {
|
||||
pub const unsafe fn new(base: usize, irq: IrqNumber) -> Self {
|
||||
Self {
|
||||
inner: InitOnce::new(),
|
||||
ring: CharRing::new(),
|
||||
base,
|
||||
irq,
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::dev::{
|
||||
serial::{pl011::Pl011, SerialDevice},
|
||||
Device,
|
||||
};
|
||||
use crate::fs::devfs;
|
||||
use crate::fs::devfs::{self, CharDeviceType};
|
||||
use crate::mem::phys;
|
||||
use error::Errno;
|
||||
|
||||
@@ -47,7 +47,7 @@ pub fn init_board() -> Result<(), Errno> {
|
||||
GIC.enable()?;
|
||||
|
||||
UART0.init_irqs()?;
|
||||
devfs::add_char_device("uart0", &UART0)?;
|
||||
devfs::add_char_device(&UART0, CharDeviceType::TtySerial)?;
|
||||
|
||||
RTC.enable()?;
|
||||
RTC.init_irqs()?;
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use core::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Config {
|
||||
cmdline: ConfigString<256>,
|
||||
console: ConfigString<16>,
|
||||
mem_limit: usize,
|
||||
initrd_base: usize,
|
||||
initrd_size: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ConfigKey {
|
||||
Cmdline,
|
||||
Console,
|
||||
MemLimit,
|
||||
InitrdBase,
|
||||
InitrdSize
|
||||
}
|
||||
|
||||
struct ConfigString<const N: usize> {
|
||||
buf: [u8; N],
|
||||
len: usize,
|
||||
}
|
||||
|
||||
pub static CONFIG: IrqSafeSpinLock<Config> = IrqSafeSpinLock::new(Config::default());
|
||||
|
||||
impl const Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
cmdline: ConfigString::empty(),
|
||||
console: ConfigString::empty(),
|
||||
mem_limit: usize::MAX,
|
||||
initrd_base: 0,
|
||||
initrd_size: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn set_usize(&mut self, key: ConfigKey, value: usize) {
|
||||
match key {
|
||||
ConfigKey::InitrdBase => { self.initrd_base = value }
|
||||
ConfigKey::InitrdSize => { self.initrd_size = value }
|
||||
ConfigKey::MemLimit => { self.mem_limit = value }
|
||||
_ => panic!("Invalid usize key: {:?}", key)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_str(&mut self, key: ConfigKey, value: &str) {
|
||||
match key {
|
||||
ConfigKey::Cmdline => { self.cmdline.set_from_str(value) }
|
||||
_ => panic!("Invalid str key: {:?}", key)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_usize(&self, key: ConfigKey) -> usize {
|
||||
match key {
|
||||
ConfigKey::InitrdBase => self.initrd_base,
|
||||
ConfigKey::InitrdSize => self.initrd_size,
|
||||
ConfigKey::MemLimit => self.mem_limit,
|
||||
_ => panic!("Invalid usize key: {:?}", key)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_str(&self, key: ConfigKey) -> &str {
|
||||
match key {
|
||||
ConfigKey::Cmdline => self.cmdline.as_str(),
|
||||
ConfigKey::Console => self.console.as_str(),
|
||||
_ => panic!("Invalid str key: {:?}", key)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_cmdline(&self, cmdline: &str) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> ConfigString<N> {
|
||||
pub const fn empty() -> Self {
|
||||
Self {
|
||||
buf: [0; N],
|
||||
len: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
core::str::from_utf8(&self.buf[..self.len]).unwrap()
|
||||
}
|
||||
|
||||
pub fn set_from_str(&mut self, data: &str) {
|
||||
let bytes = data.as_bytes();
|
||||
self.buf[..bytes.len()].copy_from_slice(&bytes);
|
||||
self.len = bytes.len();
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> fmt::Debug for ConfigString<N> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", self.as_str())
|
||||
}
|
||||
}
|
||||
@@ -88,7 +88,7 @@ fn find_node<'a>(at: INode<'a>, path: &str) -> Option<INode<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
fn find_prop<'a>(at: INode<'a>, name: &str) -> Option<IProp<'a>> {
|
||||
pub fn find_prop<'a>(at: INode<'a>, name: &str) -> Option<IProp<'a>> {
|
||||
at.props().find(|p| p.name().unwrap() == name)
|
||||
}
|
||||
|
||||
|
||||
+31
-4
@@ -1,7 +1,13 @@
|
||||
use vfs::{Vnode, VnodeKind, CharDevice, VnodeRef, CharDeviceWrapper};
|
||||
use alloc::boxed::Box;
|
||||
use crate::util::InitOnce;
|
||||
use alloc::boxed::Box;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use error::Errno;
|
||||
use vfs::{CharDevice, CharDeviceWrapper, Vnode, VnodeKind, VnodeRef};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CharDeviceType {
|
||||
TtySerial,
|
||||
}
|
||||
|
||||
static DEVFS_ROOT: InitOnce<VnodeRef> = InitOnce::new();
|
||||
|
||||
@@ -13,8 +19,9 @@ pub fn root() -> &'static VnodeRef {
|
||||
DEVFS_ROOT.get()
|
||||
}
|
||||
|
||||
pub fn add_char_device(name: &str, dev: &'static dyn CharDevice) -> Result<(), Errno> {
|
||||
debugln!("Add device: {}", name);
|
||||
fn _add_char_device(dev: &'static dyn CharDevice, name: &str) -> Result<(), Errno> {
|
||||
infoln!("Add char device: {}", name);
|
||||
|
||||
let node = Vnode::new(name, VnodeKind::Char, 0);
|
||||
node.set_data(Box::new(CharDeviceWrapper::new(dev)));
|
||||
|
||||
@@ -22,3 +29,23 @@ pub fn add_char_device(name: &str, dev: &'static dyn CharDevice) -> Result<(), E
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_char_device(dev: &'static dyn CharDevice, kind: CharDeviceType) -> Result<(), Errno> {
|
||||
static TTYS_COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||
let mut buf = [0u8; 32];
|
||||
|
||||
let (count, prefix) = match kind {
|
||||
CharDeviceType::TtySerial => (&TTYS_COUNT, b"ttyS"),
|
||||
};
|
||||
|
||||
let value = count.fetch_add(1, Ordering::Relaxed);
|
||||
if value > 9 {
|
||||
panic!("Too many character devices of type {:?}", kind);
|
||||
}
|
||||
buf[..prefix.len()].copy_from_slice(prefix);
|
||||
buf[prefix.len()] = (value as u8) + b'0';
|
||||
|
||||
let name = core::str::from_utf8(&buf[..=prefix.len()]).map_err(|_| Errno::InvalidArgument)?;
|
||||
|
||||
_add_char_device(dev, name)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
use crate::config::{ConfigKey, CONFIG};
|
||||
use crate::fs::{devfs, MemfsBlockAlloc};
|
||||
use crate::mem;
|
||||
use crate::proc::{elf, Process};
|
||||
use memfs::Ramfs;
|
||||
use vfs::{FileMode, Filesystem, Ioctx};
|
||||
|
||||
#[inline(never)]
|
||||
pub extern "C" fn init_fn(_arg: usize) -> ! {
|
||||
let proc = Process::current();
|
||||
|
||||
debugln!("Running kernel init process");
|
||||
|
||||
let cfg = CONFIG.lock();
|
||||
let initrd_start = cfg.get_usize(ConfigKey::InitrdBase);
|
||||
let initrd_size = cfg.get_usize(ConfigKey::InitrdSize);
|
||||
let console = cfg.get_str(ConfigKey::Console);
|
||||
|
||||
if initrd_start == 0 {
|
||||
panic!("No initrd specified");
|
||||
}
|
||||
|
||||
let initrd_start = mem::virtualize(initrd_start);
|
||||
let fs =
|
||||
unsafe { Ramfs::open(initrd_start as *mut u8, initrd_size, MemfsBlockAlloc {}).unwrap() };
|
||||
let root = fs.root().unwrap();
|
||||
|
||||
let ioctx = Ioctx::new(root);
|
||||
|
||||
let node = ioctx.find(None, "/init", true).unwrap();
|
||||
let mut file = node.open().unwrap();
|
||||
|
||||
proc.set_ioctx(ioctx);
|
||||
|
||||
// Open stdout
|
||||
{
|
||||
let devfs_root = devfs::root();
|
||||
let tty_node = if console.is_empty() {
|
||||
devfs_root.lookup("ttyS0")
|
||||
} else {
|
||||
devfs_root.lookup(console)
|
||||
}.expect("Failed to open stdout for init process");
|
||||
|
||||
let mut io = proc.io.lock();
|
||||
// TODO fd cloning?
|
||||
io.place_file(tty_node.open().unwrap()).unwrap();
|
||||
}
|
||||
|
||||
drop(cfg);
|
||||
|
||||
Process::execve(|space| elf::load_elf(space, &mut file), 0).unwrap();
|
||||
panic!("Unreachable");
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
const_raw_ptr_deref,
|
||||
const_fn_fn_ptr_basics,
|
||||
const_fn_trait_bound,
|
||||
const_trait_impl,
|
||||
const_panic,
|
||||
panic_info_message,
|
||||
alloc_error_handler,
|
||||
@@ -27,6 +28,7 @@ extern crate alloc;
|
||||
pub mod debug;
|
||||
|
||||
pub mod arch;
|
||||
pub mod config;
|
||||
pub mod dev;
|
||||
pub mod fs;
|
||||
pub mod mem;
|
||||
@@ -35,6 +37,8 @@ pub mod sync;
|
||||
#[allow(missing_docs)]
|
||||
pub mod syscall;
|
||||
pub mod util;
|
||||
#[allow(missing_docs)]
|
||||
pub mod init;
|
||||
|
||||
#[panic_handler]
|
||||
fn panic_handler(pi: &core::panic::PanicInfo) -> ! {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Physical memory management facilities
|
||||
|
||||
use crate::config::{ConfigKey, CONFIG};
|
||||
use crate::mem::PAGE_SIZE;
|
||||
use core::mem::size_of;
|
||||
use error::Errno;
|
||||
@@ -133,12 +134,13 @@ pub unsafe fn init_from_iter<T: Iterator<Item = MemoryRegion> + Clone>(iter: T)
|
||||
// Step 3. Initialize the memory manager with available pages
|
||||
let mut manager = ManagerImpl::initialize(mem_base, pages_base, total_pages);
|
||||
let mut usable_pages = 0usize;
|
||||
let cfg = CONFIG.lock();
|
||||
'l0: for region in iter {
|
||||
for addr in (region.start..region.end).step_by(PAGE_SIZE) {
|
||||
if !reserved::is_reserved(addr) {
|
||||
manager.add_page(addr);
|
||||
usable_pages += 1;
|
||||
if usable_pages == MAX_PAGES {
|
||||
if usable_pages >= cfg.get_usize(ConfigKey::MemLimit) {
|
||||
break 'l0;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-47
@@ -1,6 +1,7 @@
|
||||
//! Process and thread manipulation facilities
|
||||
|
||||
use crate::mem;
|
||||
use crate::init;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use alloc::{boxed::Box, collections::BTreeMap};
|
||||
|
||||
@@ -57,53 +58,8 @@ pub(self) static PROCESSES: IrqSafeSpinLock<BTreeMap<Pid, ProcessRef>> =
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: May only be called once.
|
||||
pub unsafe fn enter(initrd: Option<(usize, usize)>) -> ! {
|
||||
pub unsafe fn enter() -> ! {
|
||||
SCHED.init();
|
||||
let initrd = Box::into_raw(Box::new(initrd));
|
||||
|
||||
spawn!(fn (initrd_ptr: usize) {
|
||||
use memfs::Ramfs;
|
||||
use vfs::{Filesystem, Ioctx, FileMode};
|
||||
use crate::fs::{MemfsBlockAlloc, devfs};
|
||||
debugln!("Running kernel init process");
|
||||
|
||||
let initrd = unsafe { *(initrd_ptr as *const Option<(usize, usize)>) };
|
||||
if let Some((start, end)) = initrd {
|
||||
let proc = Process::current();
|
||||
let size = end - start;
|
||||
let start = mem::virtualize(start);
|
||||
|
||||
infoln!("Constructing initrd filesystem in memory, this may take a while...");
|
||||
let fs = unsafe {
|
||||
Ramfs::open(start as *mut u8, size, MemfsBlockAlloc {}).unwrap()
|
||||
};
|
||||
infoln!("Done constructing ramfs");
|
||||
let root = fs.root().unwrap();
|
||||
let devfs_root = devfs::root();
|
||||
|
||||
let dir = root.mkdir("dev", FileMode::default_dir()).unwrap();
|
||||
dir.mount(devfs_root.clone()).unwrap();
|
||||
|
||||
let ioctx = Ioctx::new(root);
|
||||
|
||||
// Open a test file
|
||||
let node = ioctx.find(None, "/init", true).unwrap();
|
||||
let mut file = node.open().unwrap();
|
||||
|
||||
proc.set_ioctx(ioctx);
|
||||
|
||||
// Open stdout
|
||||
{
|
||||
let mut io = proc.io.lock();
|
||||
let node = io.ioctx().find(None, "/dev/uart0", true).unwrap();
|
||||
// TODO fd cloning?
|
||||
io.place_file(node.open().unwrap()).unwrap();
|
||||
}
|
||||
|
||||
Process::execve(|space| elf::load_elf(space, &mut file), 0).unwrap();
|
||||
} else {
|
||||
infoln!("No initrd, exiting!");
|
||||
}
|
||||
}, initrd as usize);
|
||||
SCHED.enqueue(Process::new_kernel(init::init_fn, 0).unwrap().id());
|
||||
SCHED.enter();
|
||||
}
|
||||
|
||||
+2
-2
@@ -71,9 +71,9 @@ impl<T> DerefMut for IrqSafeSpinLockGuard<'_, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for IrqSafeSpinLockGuard<'_, T> {
|
||||
impl<T: fmt::Debug> fmt::Debug for IrqSafeSpinLockGuard<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", &*self)
|
||||
fmt::Debug::fmt(unsafe { &*self.lock.value.get() }, f)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user