Introduction
In the exercise of Chapter 1, a programming exercise is proposed: use system calls to sleep for 5 seconds.
Analysis
Actually, LibOS
cannot run and manage multiple processes at the same time, so we can suspend the CPU directly. But in Linux, the sleep
function is implemented by the alarm
system call, which is a little complex.
In RISC-V SBI, the sbi_hart_suspend
function can change the state of the Hardware Thread (hart) to suspend (low power) mode, and the hart will not consume any CPU cycles until it is resumed.
In RISC-V SBI Documentation, sbi_hart_suspend
is defined detailedly, here is the simplified explanation:
The signature of sbi_hart_suspend
is as follows:
|
|
suspend_type
is the type of the suspend operation, two common types are retentive and none-retentive. A retentive suspend state will preserve hart register and CSR values for all privilege modes whereas a non-retentive suspend state will not preserve hart register and CSR values. In this exercise, we use the retentive suspend state, because it’s easier to implement.
resume_addr
is the address to resume the hart. When the hart is resumed, it will start executing from this address. However, it unused during retentive suspend.
opaque
is an XLEN-bit value which will be set in the a1
register when the hart resumes execution at resume_addr after a non-retentive suspend.
After that, the hart will be suspended, but how to resume it?
In many CPUs, there is a timer interrupt that can be used to wake up the CPU. In RISC-V SBI, the sbi_set_timer
function can be used to set the timer interrupt. The signature of the sbi_set_timer
function is as follows:
|
|
stime_value
is the value to set the timer interrupt. The unit is the number of cycles of the timebase frequency of the CPU. Don’t forget it’s an absolute value, not a relative value.
So before we call sbi_hart_suspend
, we need to get the current time and add 5 seconds to it, then set the timer interrupt to this value. In addition, the full detail of sbi_set_timer
can be found in RISC-V SBI Documentation.
In RISC-V, Control and status register (CSR) is a register that stores various information in CPU. The mtime
CSR stores the number of clock cycles since the CPU started, and we can calculate the stime_value
by adding the number of cycles of 5 seconds to the value of the mtime
CSR.
stime_value = mtime + 5 * timebase_frequency
Solution
Get the Timebase Frequency
At first. we must know the timebase frequency of the CPU. For QEMU, we can get it by decompling the device tree blob (DTB) file.
|
|
0x989680 is the timebase frequency of QEMU, in decimal, it is 10,000,000.
Add Dependencies
We need to add the riscv
crate as a dependency to read mtime
.
|
|
Implementation
|
|
In this implementation, we use the riscv::register::time::read()
function to get mtime
. Then we calculate wake_up_stime
by adding the number of cycles of a certain seconds to the value of the mtime
CSR. After that, we set the timer interrupt to this value by calling sbi_set_timer
. Finally, we call sbi_hart_suspend
to suspend the hart.
Because we use the retentive suspend state, the resume_addr
and opaque
parameters are not used, we can set them to any value.
Use and Test
|
|
After that, we can try to use the suspend_secs
function to suspend the hart for 5 seconds. I tested it in QEMU, and it works well.
Summary
In this exercise, we implement the suspend_secs
function to suspend the hart for a certain number of seconds. We use the riscv
crate to read the mtime
CSR and calculate the value of the timer interrupt. By using the RISC-V SBI functions, we can suspend the hart and resume it after a certain time. This is a basic skill for operating system development.
To be honest, this problem is a little difficult for me, a newbie in operating systems. I solved it by reading many articles and documents. But I still think it’s a good exercise for learning operating systems. I will continue to read the rCore Tutorial Book and write notes about it.