So I'm trying to use the RISC-V SBI debug console extension of OpenSBI on Qemu using inline Rust assembly, but it isn't working. I've come to learn that the issue may be because of memory placement, but I'd still like to know if my assembly is correct to narrow my troubleshooting down, even if you don't know the SBI specification, I'd appreciate knowing if my general inline assembly format is correct:
pub fn debug_console_write(text: &str) -> SBIResult {
let mut sbi_result = SBIResult::new();
unsafe {
asm!(
"li a7, 0x4442434E", // Specifies SBI extension
"li a6, 0x00", // Specifies function ID in extension
"lw a0, ({})", // Provides number of bytes in string
"lw a1, ({})", // Provides pointer to string
"li a2, 0", // Provides the high end of the pointers address which should always be 0
"ecall", // Short for "enviroment call", in this case it's a call to the sbi
in(reg) &text.len(), // Provides number of bytes in string
in(reg) &text.as_ptr(), // Provides pointer to string
lateout("a0") sbi_result.error, // Puts error value from sbi call into result struct
lateout("a1") sbi_result.value, // Puts value value from sbi call into result struct
out("a7") _, // Clobbering a7
out("a6") _, // Clobbering a6
out("a2") _, // Clobbering a2
clobber_abi("system") // Clobbering everything else that the system specifcation specifies
);
}
return sbi_result
}
Note that the error and value fields are both isize.
code block(triple backticks), not just linking to it. When you edit, you should probably also add the [inline-assembly] tag. I think Rust's inline asm works similarly to GNU C inline asm, where you need to declare clobbers for any explicit register names you write, so the compiler doesn't pick them as inputs and it knows they're modified. Your code doesn't do that. It also doesn't have a"memory"clobber or memory input to tell the compiler that pointed-to memory is also an input."lw a0, ({})" ... in(reg) &text.len()put the address of the length into a register, and load the length from memory? Seems weird; since the syntax seems to support asking for operands in registers, can't you just ask forin("a0")with the Rust expression whose value you want in that register? Oh also, you write a6 and a7 before reading any of your reg inputs. That also wouldn't be a problem if you asked for your inputs in the regs you want them in, and didn't do your ownlw. Thelifor constants could also be replaced withinoperands.in(reg) &text.len()puts the address of the length in a register, which is probably not what OP intended. Memory clobber is the default in Rust though and needs to be opted-out.lwto work.