Why no output from printf in user mode?

I have a HiFive rev b and I’m trying out different things out in assembly. I’m using the command line environment riscv64-unknown-elf-gcc-8.2.0-2019.02.0-x86_64-apple-darwin and the latest version of freedom-e-sdk.

In machine mode I have no problem what so ever to call printf from assembly, but I get no output when calling printf from user mode, nor do I get any output when calling printf in the isr after causing a fault in user mode. What could be the reason?

I have tested several things to find the issue:

  • I can call printf successfully from machine mode
  • I can read, write and execute in user mode
  • I have expanded the stack space
  • I have called other methods written in C and there is not problem calling a method such as “int addition(int a, int b)”. Adding printf gives no output.
  • if I induce a fault in machine mode then a call to printf in the isr works
  • if I induce a fault in user mode then a call to printf gives no printout
  • my own methods using the uart works in user mode
  • I have checked that the registers such as gp etc have to same values as in machine mode

Here is my C code that calls the assembly:

Blockquote
#include <stdio.h>
extern void assembler();

int main() {
assembler();

return 0;
}

Here is my assembly ( I don’t know what to do to prevent comments to be interpreted as titles):

.global assembler
.section .text

.equ pmp_A, 8 # 0b01 << 3, TOR
.equ pmp_R, 1 # 1, read
.equ pmp_W, 2 # 2, write
.equ pmp_X, 4 # 4, execute
.equ PMP_CONFIG_0, (pmp_A | pmp_X | pmp_W | pmp_R)

.equ mstatus_mprv, 0x20000 # 1 << 17, mprv = modify privilege
.equ mstatus_mpp, 0x00000 # 0b00 << 11, mpp = privilege mode = user mode
.equ MSTATUS_MODPRIV, (mstatus_mprv | mstatus_mpp)

.equ mstatus_mpp_mask, 0x01800 # 3 << 11
.equ mstatus_mpp_user, 0 # 0b00 = usermode

assembler:
la a0, mhello
call printf

establish the interrupt service route

la t0, fault
csrw mtvec, t0

every address, below max

li t0, -1
srli t0, t0, 2
csrw pmpaddr0, t0

from TOR and below, read, write and execute in user mode

li t0, PMP_CONFIG_0
csrw pmpcfg0, t0

check protection for user mode according to pmp configuration

csrr t1, mstatus
li t0, MSTATUS_MODPRIV
or t0, t0, t1
csrw mstatus, t0

setup user mode registers

csrr t0, mstatus
li t1, mstatus_mpp_mask
not t1, t1
and t0, t0, t1
li t1, mstatus_mpp_user
or t0, t0, t1
csrw mstatus, t0 # indicate that we want user mode

la sp, ustack
la t0, usermode
csrw mepc, t0
la ra, usermode
mret # “return” to previous mode (carefully set up to be user mode)

la a0, wtf
call print_string

ret

usermode:
la a0, hello
call printf

100:
j 100b

fault:

This fails if something happens in user mode…

csrr a2, mstatus
csrr a1, mcause
la a0, error
call printf

100:
j 100b

hello:
.string “Hello from user mode.\r\n”
mhello:
.string “Hello from machine mode.\r\n”
wtf:
.string “\033[91mWTF?\r\n”
error:
.string “\033[91mInterrupted, cause: 0x%x, mstatus: 0x%x\r\n”

.section .data
.balign 16

test:
.word 0xdeadbeef

guard:
.word 0xdeadbeef # if the stack has been overrun, then this guard will not say 0xdeadbeef anymore

.space 24*4
ustack:

User mode is user mode – it has no privileges. If you want to do something like I/O from User mode then it is generally your responsibility to ensure that you have installed some kind of system call service in machine mode that User mode can use, typically by installing a handler for the ECALL instruction and defining some kind of SBI.

I’m not sure of the point of this. It’s not going to do anything useful for the mret, and will only result in the ret after printing wtf (if that somehow happens, which it shouldn’t) jumping to print “Hello from user mode”. In machine mode.

Than you for your answer.
My goal is to use ecall to create a set of system services. This is me getting closer.

What do you mean with privileges in this case? What is it that user mode is lacking? It can access all the memory, including the uart. When I swap printf with my own code that prints using the uart it works great in user mode.

Thank you for taking time to read my code. I should have removed this instruction. It’s a left over from me experimenting and trying to find the answer to why printf does not print anything. As you say, it doesn’t add anything.