The RISC-V Linux User's Manual

Quan Nguyen

March 21, 2013

This document is out-of-date and was never authoritative. This is not an official source for RISC-V information. It is here only historical purposes. Please consult riscv.org for the most up-to-date information.

RISC-V Linux User's Manual proposed cover
Proposed cover of the RISC-V Linux User's Manual

Introduction

The purpose of this page is to document the assumptions made during the development of the RISC-V port of the Linux kernel.

A project with a duration such as this requires adequate documentation to support future development and maintenance. This document is created with the hope of being useful; however, its accuracy is not guaranteed.

Copyright Information

This document is currently under development. This document is copyrighted by Quan Nguyen, © 2013. All rights reserved. I do intend, however, to release this document to the public freely upon its satisfactory completion.

History

A full description of the history can be found in Volume I of the RISC-V Instruction Set Manual. While the architecture intends to be a clean break, its implementation contains many remnants of predecessor architectures. In particular, the port of gcc and glibc derive from MIPS; as a result, architectural decisions that were not explicitly made by the RISC-V ISA have been inherited from MIPS. The RISC-V port of Linux itself is also inspired by parts of the Linux-MIPS codebase; however, careful considerations were made to rely as little on MIPS conventions as possible.

Therefore, when this document is unclear, referring to the MIPS specification for the corresponding topic may yield insight.

The RISC-V Instruction Set Architecture (ISA)

Register Conventions

The RISC-V ISA makes visible:

For 64-bit versions of RISC-V, the XPRs are 64-bits wide, and for 32-bit versions of RISC-V, the XPRs are 32-bits wide. The privileged control registers may have different widths, depending on the nature of the register.

General-purpose Registers

The RISC-V port of the Linux kernel uses the following conventions for its registers.

RegisterCanonical NameRISC-V Linux
x0zeroZero (hard-wired)
x1raReturn address
x2v0Function return value
Syscall number
x3v1Function return value (pipe2 only)
x4a0Function argument register
x5a1Function argument register
x6a2Function argument register
x7a3Function argument register
Syscall error code
x8a4Function argument register
x9a5Function argument register
x10a6Function argument register
x11a7Function argument register
x12t0Temporary register
x13t1Temporary register
x14t2Temporary register
x15t3Temporary register
x16t4Temporary register
x17t5Temporary register
x18t6Temporary register
x19t7Temporary register
x20s0Caller-save register
x21s1Caller-save register
x22s2Caller-save register
x23s3Caller-save register
x24s4Caller-save register
x25s5Caller-save register
x26s6Caller-save register
x27s7Caller-save register
x28s8 / gpCaller-save register
Global pointer
x29s9 / fpCaller-save register
Frame pointer
x30spStack pointer (user and kernel)
x31tpThread pointer (for TLS)

MIPS convention note: The pipe2 system call is specially handled by MIPS implementations, but the RISC-V port of glibc has been patched to remove this behavior.

The RISC-V port of the Linux kernel does not use any floating-point hardware. There are no assumptions made about the values of the floating-point registers, or the status register fsr.

Privileged Control Registers

The privileged control registers (PCRs) are used by the processor in Supervisor mode to complete tasks such as memory management and exception handling. These are not accessible from userspace.

Privileged Control RegisterCanonical NameRISC-V Linux
pcr0statusProcessor Status Register
pcr1epcException Program Counter
Bad Address (Instruction faults)
pcr2badvaddrBad Virtual Address (Stores/Loads)
pcr3evecException Handler Address
pcr4countTimer counter value
pcr5compareTimer compare value
pcr6causeException cause register
pcr7ptbrPage table base register
pcr8Unused by RISC-V Linux
pcr9Unused by RISC-V Linux
pcr10Unused by RISC-V Linux
pcr11Unused by RISC-V Linux
pcr12k0Kernel scratch register
current task pointer
pcr13k1Kernel scratch register
pcr14Usused by RISC-V Linux
pcr15Usused by RISC-V Linux
pcr16Usused by RISC-V Linux
pcr17Usused by RISC-V Linux
pcr18Usused by RISC-V Linux
pcr19Usused by RISC-V Linux
pcr20Unused by RISC-V Linux
pcr21Unused by RISC-V Linux
pcr22Unused by RISC-V Linux
pcr23Unused by RISC-V Linux
pcr24Unused by RISC-V Linux
pcr25Unused by RISC-V Linux
pcr26tosimTo-Simulator Register
pcr27fromsimFrom-Simulator Register
pcr28Unused by RISC-V Linux
pcr29Unused by RISC-V Linux
pcr30tohostHTIF To-Host Communication Register
pcr31fromhostHTIF From-Host Register

Simulator implementation note: The tosim and fromsim registers are magical PCRs that do special commands based on the packet whose physical address is written to these registers.

The status PCR

The status register has been subject to many changes in the RISC-V ISA. However, the RISC-V port of Linux uses the following configuration:

Field name IP IM 0 VM S64 U64 S PS EC EV EF ET
Bit position 3124 2316 159 8 7 6 5 4 3 2 1 0
Initial value 0 0 0 0 0 0 0 0 ? ? ? ? ? ? ? ? 0 0 0 0 0 0 0 0 1 1 1 ? ? X X 0

Shown are the values that are known at processor startup. Other than the obvious 0 and 1, a ? is undefined and X as "don't care."

Simulator implementation note: The simulator sets the the interrupt mask (IM) to all 1, previous-supervisor (PS) to 0, and all EC/EV/EF to 0.

Interrupts and Exceptions

All interrupts (and exceptions) enter through one exception handler, the address of which is located in the evec PCR.

Interrupts

RISC-V provides eight interrupt lines for timers and other devices. They are level triggered, and masked with the IM field in the status PCR. The interrupts in use in the RISC-V port of Linux are as follows:

Interrupt NumberDescription
0Unused
1Unused
2Unused
3Unused
4Unused
5Inter-processor
6Host/Devices
7Timer

Interrupt priority decreases as the interrupt number increases; in this case, the timer interrupt is always taken last. Each interrupt is associated with an exception number, to be placed in the cause PCR.

Simulator implementation note: The ISA functional simulator differs from this behavior by setting the topmost bit of the cause register if it is an interrupt.

Exceptions

Exceptions are taken as the faulting instruction is executed. The behavior of each exception is different, particularly in the semantics of how error values are saved. The list is not comprehensive, but covers the most useful registers.

If an exception is not handled by Linux, the kernel will panic.

Code
(cause)
DescriptionValue of other registersHandled in
RISC-V Linux?
0Instruction address misalignedepc = misaligned instruction address
badvaddr = misaligned instruction address
No
1Instruction access faultepc = faulting instruction addressYes
2Illegal instructionepc = illegal instruction addressNo
3Privileged instructionepc = privileged instruction addressNo
4Floating point disabledepc = floating point instruction addressNo
5Reserved(Reserved)No
6System callepc = system call instruction address
v0 = system call number
(See "System Call conventions")
Yes
7Breakpointepc = break instruction addressNo
8Load address misalignedepc = misaligned load instruction address
badvaddr = misaligned load address
No
9Store address misalignedepc = misaligned store instruction address
badvaddr = misaligned store address
No
10Load access faultepc = faulting load instruction address
badvaddr = faulting load address
Yes
11Store access faultepc = faulting store instruction address
badvaddr = faulting store address
Yes
12-15Unused(Not applicable)No
16-23Interruptscause not equal to (interrupt number + 16)
(See "Interrupts and Exceptions")
Some
24-31Unused(Not applicable)No

ISA oddity note: Most exceptions that can be handled by Linux return to exactly epc to repeat the instruction. However, this behavior is not desirable for system calls or breakpoints. The functional simulator does not distinguish system calls or breakpoints from other exceptions, and any use of the eret instruction will repeatedly jump on the syscall or break instructions. Therefore, for these two exceptions, the Linux exception handler will manually increment epc by one instruction forward (epc <- epc + 0x4) before returning to the program that triggered the exception.

The RISC-V Application Binary Interface (ABI)

MIPS convention note: The RISC-V ABI, in both its forms, RV32 and RV64, share many commonalites with their respective MIPS counterparts; MIPS O32 and MIPS64. Yunsup notes that RISC-V also takes cues from the MIPS Embedded ABI (EABI). However, it is the conclusion of the author that some ABI decisions made by MIPS developers were brain-damaged unsuitable for RISC-V to make a "clean break" from previous architectures.

System Call (syscall) conventions

Entering a syscall

The number of the syscall can be found in arch/riscv/asm/include/unistd.h, but it includes the file include/linux/asm-generic/unistd.h, so users may refer to that instead.

Syscalls currently may have up to six arguments to be used. RISC-V provides eight argument registers, so all arguments may be passed via registers a0 through a7.

This invokes the kernel handler for syscalls.

Returning from a syscall

Linux returns the result of the syscall in register v0.

MIPS covention note: MIPS convention also specifies that register a3 contains the error status of the syscall: 0 if successful and less than zero otherwise. This error status is set by the architecture-dependent portion of the kernel; however, glibc seems to expect register a3 to be clobbered. (Why a3? MIPS has only four argument registers, but this notion was not preserved when RISC-V was designed with eight argument registers.)

To recover the result,

Again, this stipulates that a3 is clobbered.

Function calling conventions

As with MIPS, the following registers must be saved by the caller:

And these registers must be saved by the callee:

The author presumes that twiddling with sp or tp will be very bad ideas.

Error Codes

Various things provide error codes for failed system calls or kernel functions. The field below provides a lookup table for the error values for the RISC-V port of Linux. The file can also be found at include/asm-generic/errno-base.h and include/asm-generic/errno.h.

Errno lookup

Error number:

(Enter an error number)

Kernal Internals

Context Switching

When the processor enters kernel mode, it must save the entire state of the user process. The struct task_struct pointer of the current process is always located in the k0 PCR. When the processor needs to save the user process's state, it saves the following registers, in this order:

  1. sp (the user stack pointer)
  2. all user registers except sp
  3. status PCR
  4. epc PCR

The registers are saved into the kernel-mode stack for the process, located at current->stack, where current also refers to the k0 PCR.