-
-
Notifications
You must be signed in to change notification settings - Fork 849
Initializing UART driver causes execution to freeze. #121
-
My setup for this is slightly different from the one shown, but only slightly. I'm using aarch64-none-elf-gcc
to compile the assembly file and the using -C link-arg=$(BOOT_OBJ)
as an argument to rustc
to link the object file into the final ELF.
Up until tutorial 05, everything was working fine with QEMU.
Now after introducing tock-registers
I'm having trouble initializing the UART driver.
I've narrowed the problem down to this after a couple days of frustration. When I try to access any register, like the control register or flags register, the execution freezes, maybe because of an infinite nop
loop?
For example, in the flush
function, accessing FR causes a freeze
fn flush(&self) { // Execution doesn't enter the loop because on checking the FR, it freezes while self.registers.FR.matches_all(FR::BUSY::SET) { cpu::nop(); } }
Similarly, if I comment everything else out from the init
function and just have the following line,
self.registers.CR.set(0); // this also causes execution to freeze
I'm able to write to the same memory location with write_volatile
and that doesn't cause any problems... for example for the CR
// this works unsafe { core::ptr::write_volatile(0x3F20_1030 as *mut usize, (0b1 << 31)); }
I'm now out of ideas on what to try, I've tried looking at the disassembly with aarch64-none-elf-objdump
and that also doesn't give me any immediate clues. I've even compared every line of my diff to the diff in the readme and its almost all the same except for how I've structured my files. I've even tried structuring my files exactly like in the tutorial but alas to no avail.
Here is my repository if that helps. Right now I've modified the println
macro and panic handler to print to QEMU instead of actual hardware since testing with the Pi was becoming a huge pain.
Beta Was this translation helpful? Give feedback.
All reactions
The problem was being caused by a few misplaced #[cfg(feature = "...")]
Replies: 3 comments 11 replies
-
The GitHub actions CI has tests for the UART output as provided by QEMU, and it seems that still works, so I assume it must be something local on your setup.
Unsure when I will find time to look at it, but I’ll try to check it when I can.
Beta Was this translation helpful? Give feedback.
All reactions
-
https://github.com/zrthxn/pios/blob/master/src/bsp/raspi/memory.rs#L16
Why is it 7e
here? Compare: https://github.com/rust-embedded/rust-raspberrypi-OS-tutorials/blob/master/08_hw_debug_JTAG/src/bsp/raspberrypi/memory.rs#L23
Beta Was this translation helpful? Give feedback.
All reactions
-
That's outdated slightly, that was an experiment. 7e201000 is the physical address of the UART while 3f201000 is the bus address. I was trying that to see if it works but it doesn't. But also doesn't work with 3f20.
I'll just push what have currently.
Beta Was this translation helpful? Give feedback.
All reactions
-
@andre-richter I've just tried copy-pasting your source code from tutorial 05 (the whole src
folder) without modification and used the following Makefile with it. The Cargo.toml
is the exact same and so is the rust-toolchain.toml
.
After building it with make kernel
and Running it on real hardware and connection USB serial to it and using miniterm, I get nothing printed to the console.
# ===================================== # Variables---------------------------- LINKER_FILE = src/bsp/raspberrypi/link.ld TARGET_ARCH = aarch64 TARGET_CPU = cortex-a53 TARGET_RUSTC = aarch64-unknown-none-softfloat KERNEL_ELF = target/$(TARGET_RUSTC)/release/pios KERNEL_BIN = target/$(TARGET_RUSTC)/release/kernel8.img # ===================================== # Cmdlets ----------------------------- # rustc compiler RUSTC_CMD = cargo rustc $(RUSTC_ARGS) RUSTC_ARGS = \ --target=$(TARGET_RUSTC) \ --features bsp_rpi3 \ --release \ RUSTFLAGS = -C target-cpu=$(TARGET_CPU) -C link-arg=-T$(LINKER_FILE) # QEMU run OBJCOPY = rust-objcopy --strip-all -O binary QEMU_CMD = qemu-system-aarch64 $(QEMU_ARGS) QEMU_ARGS = \ -M raspi3 \ -display none \ -serial stdio \ # ===================================== # Targets ----------------------------- .PHONY: all build kernel clean qemu all: build kernel clean build: RUSTFLAGS="$(RUSTFLAGS)" $(RUSTC_CMD) kernel: build $(OBJCOPY) $(KERNEL_ELF) $(KERNEL_BIN) qemu: build kernel $(QEMU_CMD) -kernel $(KERNEL_BIN)
Beta Was this translation helpful? Give feedback.
All reactions
-
Does it work with QEMU?
Beta Was this translation helpful? Give feedback.
All reactions
-
No it doesn't. I put my qemu.rs
module (which is basically a copy of what you had) in the bsp
module and changed panic and println macros to use that. Then I modified kernel_init
to have some printlns
println!("[-] Init Boot");
for i in bsp::driver::driver_manager().all_device_drivers().iter() {
if let Err(x) = i.init() {
panic!("Error loading driver: {}: {}", i.compatible(), x);
}
}
bsp::driver::driver_manager().post_device_driver_init();
// println! is usable from here on.
println!("[-] Done");
The first print works, the second one doesn't. Indicating the same issue. So there's probably no issue with my code?
Beta Was this translation helpful? Give feedback.
All reactions
-
A little update which doesn't really help a great deal but still something to consider maybe.
So I was talking to someone at Tock and they said that setting some of those registers might be triggering an exception handler... which might be possible although I'm no expert. What I've tried is writing those register memory locations with write_volatile
in the init
function of the UART driver.
pub fn init(&mut self) { // self.flush(); unsafe { let fr = read_volatile(0x3f20_1018 as *mut u32); while (fr & (0b1<<3 as u32)) != 0 { cpu::nop(); } } // self.registers.CR.set(0); unsafe { write_volatile(0x3f20_1030 as *mut u32, 0 as u32) }; // self.registers.ICR.write(ICR::ALL::CLEAR); unsafe { write_volatile(0x3f20_1044 as *mut u32, 0 as u32) }; // Set the baud rate, 8N1 and FIFO enabled. unsafe { // self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(3)); write_volatile(0x3f20_1024 as *mut u32, ( read_volatile(0x3f20_1024 as *mut u32) & 0b1111111111111111 )); // self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(16)); write_volatile(0x3f20_1028 as *mut u32, ( read_volatile(0x3f20_1028 as *mut u32) & 0b111111 )); // self.registers.LCR_H.write(LCR_H::WLEN::EightBit + LCR_H::FEN::FifosEnabled); write_volatile(0x3f20_102c as *mut u32, ( (read_volatile(0x3f20_102c as *mut u32) & 0b11<<5) + (read_volatile(0x3f20_102c as *mut u32) & 0b1<<4) )); } // Turn the UART on. // self.registers.CR.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); unsafe { write_volatile(0x3f20_1030 as *mut u32, ( (0b1<<9 + 0b1<<8 + 0b1) as u32 )); } }
This doesn't cause any issues as such which I suppose is expected. But I still don't know if this is right since this might not be doing the exact same things as tock-registers
.
But still, since the code in the tutorial works on my setup, it still feels like this might be something wrong with my setup.
Beta Was this translation helpful? Give feedback.
All reactions
-
The problem was being caused by a few misplaced #[cfg(feature = "...")]
Beta Was this translation helpful? Give feedback.