[RISC-V] System Call
2025. 5. 3. 11:23ใComputerScience/OperatingSystem
PA2: System Call
System calls are interfaces that allow user applications to request various services from the operating system kernel. This project aims to explore and understand how system calls are implemented in the xv6 operating system.
git-hub: https://github.com/snu-csl/os-pa2/
๐ start.c
- xv6 kernel boot up code
- ๋ฆฌ๋ ์ค ์ปค๋์ ์ด๊ธฐํ ๊ณผ์ ์ ๋ด๋น
- ์ปค๋์ด ๋ถํ ๋ ๋ ๊ฐ์ฅ ๋จผ์ ์คํ๋์ด, ์ด๊ธฐ ํ๊ฒฝ์ ์ค์
#include "types.h"
#include "param.h"
#include "memlayout.h"
#include "riscv.h"
#include "defs.h"
void main();
void timerinit();
// entry.S needs one stack per CPU.
__attribute__ ((aligned (16))) char stack0[4096 * NCPU];
// entry.S jumps here in machine mode on stack0.
void
start()
{
// set M Previous Privilege mode to Supervisor, for mret.
unsigned long x = r_mstatus();
x &= ~MSTATUS_MPP_MASK;
x |= MSTATUS_MPP_S;
w_mstatus(x);
// set M Exception Program Counter to main, for mret.
// requires gcc -mcmodel=medany
w_mepc((uint64)main);
// disable paging for now.
w_satp(0);
// delegate all interrupts and exceptions to supervisor mode.
w_medeleg(0xffff);
w_mideleg(0xffff);
w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE);
#ifdef SNU
setpmp();
#else
// configure Physical Memory Protection to give supervisor mode
// access to all of physical memory.
w_pmpaddr0(0x3fffffffffffffull);
w_pmpcfg0(0xf);
#endif
// ask for clock interrupts.
timerinit();
// keep each CPU's hartid in its tp register, for cpuid().
int id = r_mhartid();
w_tp(id);
// switch to supervisor mode and jump to main().
asm volatile("mret");
}
// ask each hart to generate timer interrupts.
void
timerinit()
{
// enable supervisor-mode timer interrupts.
w_mie(r_mie() | MIE_STIE);
// enable the sstc extension (i.e. stimecmp).
w_menvcfg(r_menvcfg() | (1L << 63));
// allow supervisor to use stimecmp and time.
w_mcounteren(r_mcounteren() | 2);
// ask for the very first timer interrupt.
w_stimecmp(r_time() + 1000000);
}
- entry.S: ๊ฐ hart์ ๋ํ์ฌ, stack0 + hartid * 4096์ ์คํ ํฌ์ธํฐ๋ก ์ค์ -> start() ์ง์
- ๋จธ์ ๋ชจ๋์์ ๋ชจ๋ ํธ๋ฉ/์ธํฐ๋ฝํธ๋ฅผ S Mode๋ก ์์
- S Mode๊ฐ ๋ชจ๋ ๋ฉ๋ชจ๋ฆฌ์ ์ ๊ทผํ ์ ์๋๋ก PMP ์ค์ (#ifdef SNU)
- ํ์ด๋จธ ์ธํฐ๋ฝํธ ์ด๊ธฐํ
- M → S Mode ์ ํ (asm volatile("mret");)
์ปค๋์ด M-mode์์ ๋ถํ ๋๊ณ , S-mode๋ก ์ ํ๋ ํ, ๋์
๐ kernel/riscv.h
- Architecture-dependent codes
- OS์ RISC-V CPU์ interaction ์ง์
- ํ๋์จ์ด ์ถ์ํ ๋ ์ด์ด - HAL: Hardware Abstraction Layer
- ์ด์ ๋ธ๋ฆฌ ๋ช ๋ น์ด(CSR read/write, register ์ ๊ทผ) wrapping
// Physical Memory Protection
static inline void
w_pmpcfg0(uint64 x)
{
asm volatile("csrw pmpcfg0, %0" : : "r" (x));
}
static inline void
w_pmpaddr0(uint64 x)
{
asm volatile("csrw pmpaddr0, %0" : : "r" (x));
}
- csrr: CSR read
- csrw : CSR write
# Reading pmpaddr0 register
csrr a0, pmpaddr0
๐ kernel/kernelvec.S
- M-mode and S-mode trap vectors
- ์ปค๋์์ ์ธํฐ๋ฝํธ -> CPU ์ง์ ์ (Trap Vector) ์ญํ
- ์ปค๋ ํธ๋ฉ ํธ๋ค๋ฌ๋ฅผ ํธ์ถํ๋ ์ญํ
#
# Interrupts and exceptions while in supervisor mode come here.
# The current stack is a kernel stack.
# We push registers, call the kerneltrap() function.
# When kerneltrap() returns, we restore registers and return.
#
.globl kerneltrap
.globl kernelvec
.align 4
kernelvec:
# Make room to save registers.
addi sp, sp, -256
# Save caller-saved registers.
sd ra, 0(sp)
sd sp, 8(sp)
sd gp, 16(sp)
sd tp, 24(sp)
sd t0, 32(sp)
sd t1, 40(sp)
sd t2, 48(sp)
sd a0, 72(sp)
sd a1, 80(sp)
sd a2, 88(sp)
sd a3, 96(sp)
sd a4, 104(sp)
sd a5, 112(sp)
sd a6, 120(sp)
sd a7, 128(sp)
sd t3, 216(sp)
sd t4, 224(sp)
sd t5, 232(sp)
sd t6, 240(sp)
# Call the C trap handler in trap.c
call kerneltrap
# Restore registers.
ld ra, 0(sp)
ld sp, 8(sp)
ld gp, 16(sp)
ld t0, 32(sp)
ld t1, 40(sp)
ld t2, 48(sp)
ld a0, 72(sp)
ld a1, 80(sp)
ld a2, 88(sp)
ld a3, 96(sp)
ld a4, 104(sp)
ld a5, 112(sp)
ld a6, 120(sp)
ld a7, 128(sp)
ld t3, 216(sp)
ld t4, 224(sp)
ld t5, 232(sp)
ld t6, 240(sp)
addi sp, sp, 256
# Return to whatever we were doing in the kernel.
sret
- ํธ๋ฉ ๋ฐ์ -> S-Mode stvec register ์ฃผ์๋ก jump (kernelvec)
- ์คํ์์ 256 byte ๋ด๋ฆฌ๊ธฐ (addi sp, sp, -256)
๋ ์ง์คํฐ๋ฅผ ์คํ์ ๋ฐฑ์ (caller-saved register: ra, t0~t6, a0~a7, gp, tp, sp)
- ํธ๋ฉ ์ฒ๋ฆฌ ํจ์ ํธ์ถ (kerneltrap in trap.c) -> Call kerneltrap
- register ๊ฐ ๋ณต์ํ๊ธฐ
- sret = ํธ๋ฉ์ด ๋ฐ์ํ๋ ์ง์ ์ผ๋ก ๋์๊ฐ๊ธฐ
+ riscv.h - stvec์ด ์ฐ์ด๋ part
// Supervisor Trap-Vector Base Address
// low two bits are mode.
static inline void
w_stvec(uint64 x)
{
asm volatile("csrw stvec, %0" : : "r" (x));
}
static inline uint64
r_stvec()
{
uint64 x;
asm volatile("csrr %0, stvec" : "=r" (x) );
return x;
}
๐ kernel/trap.c
- Trap handling = ํธ๋ฉ ์ฒ๋ฆฌ ๋ก์ง
- ํ๋ก๊ทธ๋จ ์คํ ์ค ๋ฐ์ํ๋ Interrupt, exception, system call, trap ์ฒ๋ฆฌ
// interrupts and exceptions from kernel code go here via kernelvec,
// on whatever the current kernel stack is.
void
kerneltrap()
{
int which_dev = 0;
uint64 sepc = r_sepc();
uint64 sstatus = r_sstatus();
uint64 scause = r_scause();
if((sstatus & SSTATUS_SPP) == 0)
panic("kerneltrap: not from supervisor mode");
if(intr_get() != 0)
panic("kerneltrap: interrupts enabled");
if((which_dev = devintr()) == 0){
// interrupt or trap from an unknown source
printf("scause=0x%lx sepc=0x%lx stval=0x%lx\n", scause, r_sepc(), r_stval());
panic("kerneltrap");
}
// give up the CPU if this is a timer interrupt.
if(which_dev == 2 && myproc() != 0)
yield();
// the yield() may have caused some traps to occur,
// so restore trap registers for use by kernelvec.S's sepc instruction.
w_sepc(sepc);
w_sstatus(sstatus);
}
๐ kernel/syscall.c
- General system call handling
- User Program์ด kernel ๊ธฐ๋ฅ ์์ฒญ ์ ์ฌ์ฉํ๋ ์ธํฐํ์ด์ค
- System Call -> ํด๋นํ๋ kernel func ํธ์ถ · ์ ๋ฌ
void
syscall(void)
{
int num;
struct proc *p = myproc();
num = p->trapframe->a7;
if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
// Use num to lookup the system call function for num, call it,
// and store its return value in p->trapframe->a0
p->trapframe->a0 = syscalls[num]();
} else {
printf("%d %s: unknown sys call %d\n",
p->pid, p->name, num);
p->trapframe->a0 = -1;
}
}
- User Trap -> Syscall ํธ์ถ
- a7 register: System Call number ์ ์ฅ
- ๋งคํ๋ sys_ func ํธ์ถ -> ๋ฐํ ๊ฐ a0์ ์ ์ฅ
๐ kernel/sysproc.c
- Several system call implementations
- system call ์ค ํ๋ก์ธ์ค ๊ธฐ๋ฅ ๊ตฌํ (์ปค๋ ๋ด๋ถ ๋์ ์ ์)
- User Program์ด ์์ฒญํ๋ system call์ ๋ก์ง์ ์ปค๋์์ ๊ตฌํ
uint64
sys_exit(void)
{
int n;
argint(0, &n);
exit(n);
return 0; // not reached
}
uint64
sys_getpid(void)
{
return myproc()->pid;
}
uint64
sys_fork(void)
{
return fork();
}
uint64
sys_wait(void)
{
uint64 p;
argaddr(0, &p);
return wait(p);
}
uint64
sys_sbrk(void)
{
uint64 addr;
int n;
argint(0, &n);
addr = myproc()->sz;
if(growproc(n) < 0)
return -1;
return addr;
}
uint64
sys_sleep(void)
{
int n;
uint ticks0;
argint(0, &n);
if(n < 0)
n = 0;
acquire(&tickslock);
ticks0 = ticks;
while(ticks - ticks0 < n){
if(killed(myproc())){
release(&tickslock);
return -1;
}
sleep(&ticks, &tickslock);
}
release(&tickslock);
return 0;
}
uint64
sys_kill(void)
{
int pid;
argint(0, &pid);
return kill(pid);
}
+ ์ค์ system call ๋ด์ฉ๋ค์ ๊ตฌํํ๋ ๋ถ๋ถ
์ถ์ฒ: https://github.com/snu-csl/os-pa2/
GitHub - snu-csl/os-pa2: OS Project #2 (Fall 2024)
OS Project #2 (Fall 2024). Contribute to snu-csl/os-pa2 development by creating an account on GitHub.
github.com
'ComputerScience > OperatingSystem' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[OS Project] Chap10-1. Process - PCB and Context switch (0) | 2025.01.19 |
---|---|
[OS Project] Chap9. Memory Allocation (0) | 2025.01.18 |
[OS Project] Chap8. Exception (0) | 2025.01.17 |
[OS Project] Chap7. Kernel Panic (0) | 2025.01.17 |
[OS Project] Chap6. C Standard Library (0) | 2025.01.17 |