Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Initializing UART driver causes execution to freeze. #121

Discussion options

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.

You must be logged in to vote

The problem was being caused by a few misplaced #[cfg(feature = "...")]

Replies: 3 comments 11 replies

Comment options

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.

You must be logged in to vote
11 replies
Comment options

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.

Comment options

@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)
Comment options

Does it work with QEMU?

Comment options

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?

Comment options

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.

You must be logged in to vote
0 replies
Comment options

The problem was being caused by a few misplaced #[cfg(feature = "...")]

You must be logged in to vote
0 replies
Answer selected by zrthxn
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet

AltStyle によって変換されたページ (->オリジナル) /