0% found this document useful (0 votes)
6 views8 pages

Tutorial4 Xv6

This tutorial provides a step-by-step guide for compiling and setting up the Xv6-RISCV operating system, as well as creating and deploying a simple kernel-mode program. It outlines the prerequisites, necessary tools, and detailed instructions for adding a new system call (SYS_hello) to the kernel, including modifications to various source files. Additionally, it includes troubleshooting tips and an exercise for further practice in kernel programming.

Uploaded by

Bùi Duy Thái
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views8 pages

Tutorial4 Xv6

This tutorial provides a step-by-step guide for compiling and setting up the Xv6-RISCV operating system, as well as creating and deploying a simple kernel-mode program. It outlines the prerequisites, necessary tools, and detailed instructions for adding a new system call (SYS_hello) to the kernel, including modifications to various source files. Additionally, it includes troubleshooting tips and an exercise for further practice in kernel programming.

Uploaded by

Bùi Duy Thái
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 8

TUTORIAL 4 – PROGRAMMING KERNEL IN Xv6-RISCV

Objectives:
This tutorial guides you through:

• Compiling and setting up a Unix-like operating system – Xv6


• Creating, compiling and deploying a simple program that runs in kernel-mode

1) Prerequisites & getting the source


What you need (high level)

• A RISC-V newlib toolchain (riscv-gnu-toolchain) on your PATH. The xv6 README


points to the upstream riscv-gnu-toolchain (https://github.com/mit-pdos/xv6-
riscv)
• QEMU (a riscv64 build). MIT course notes recommend a recent QEMU (e.g. 5.x or
newer). pdos.csail.mit.edu
• Standard dev tools: git, make, gcc (host), perl (usys.pl is a perl script).

If you already have a distro package for a riscv cross toolchain / qemu, use that; otherwise
you can build the toolchain from riscv-gnu-toolchain (the README in the xv6 repo
mentions this requirement). GitHub

Example quick install (Ubuntu-ish) — adjust to your OS:

# install system packages (Ubuntu/Debian)


sudo apt update
sudo apt install -y git make build-essential perl python3 \
qemu-system-misc qemu-user \
# optional: cross toolchain packaged by distro
gcc-riscv64-unknown-elf || true

If you need the upstream GNU toolchain, follow riscv-gnu-toolchain instructions (clone
and make newlib) — this builds lots of code on your machine. The xv6 README links to the
toolchain repo. GitHub

Clone xv6-riscv:
git clone https://github.com/mit-pdos/xv6-riscv.git
cd xv6-riscv

Try to build & run the stock kernel to verify your toolchain + qemu are set up:

make clean
make qemu # builds the kernel + user programs and starts QEMU

If that gets you a shell prompt like init: starting sh or a prompt #, you’re good to go. (If make
qemu errors, the top-level README gives toolchain pointers.)

2) Preparing for a kernel program


What we will do (summary)

1. Add SYS_hello to kernel/syscall.h (pick the next free number).


2. Add an entry("hello"); line in user/usys.pl so the user stub is generated.
3. Add int hello(void); prototype to user/user.h (so user C can call it).
4. Implement the kernel side sys_hello() in kernel/sysproc.c (it will run in kernel and
call printf).
5. Add sys_hello to the syscalls[] table in kernel/syscall.c.
6. Replace your user hello program to call hello() (the syscall), rebuild and run.
7. Edit top-level Makefile, adding the program entry to UPROGS tag

This approach runs your code inside the kernel when the syscall executes.

Before you start — find the next free syscall number

Open kernel/syscall.h and note the highest #define SYS_xxx value. The new SYS_hello
must use the next integer.

Quick command to show the last SYS_ number (run from xv6 root):

grep -n "define SYS_" kernel/syscall.h | sed -n '$p'

This prints the last #define SYS_... line. If it shows e.g.


#define SYS_write 22

then the next free number is 23. Use that number in the change below.

(If your syscall.h already contains a SYS_hello — skip the add and use the existing
number.)

Add it to the Makefile

Open the top-level Makefile and find the list that begins with:

UPROGS=\
_cat\
_echo\
_forktest\

Add your new program:

_hello\

(Make sure it’s listed with a backslash at the end like the others.)

3) Concrete edits
Below are the exact file changes. Put each snippet into the named file, replacing or
appending as noted.

kernel/syscall.h — add the define

Open kernel/syscall.h and append one line near the other SYS_ defines. Use the next free
number you found above.

Example (if next number is 23):


#define SYS_hello 23

(If your last SYS_ was N, use N+1 instead of 23.)

user/usys.pl — add the stub entry

Open user/usys.pl and add entry("hello"); alongside the other entry("..."); lines.

Example context:

entry("fork");
entry("exit");
entry("wait");
...
entry("sleep");
entry("hello"); # <-- add this line

Then save it.

user/user.h — add user prototype

Open user/user.h and add the prototype (anywhere with other prototypes):

int hello(void);

kernel/sysproc.c — implement sys_hello

Edit kernel/sysproc.c. Add sys_hello near the other sys_* functions (for example next to
sys_getpid, sys_fork, etc.). Use the same include style as other functions.

Add this snippet (paste into the file in a logical spot — e.g., after other sys_ functions):
#include "types.h"
#include "riscv.h"
#include "defs.h"
#include "param.h"
#include "memlayout.h"
#include "proc.h"

// Kernel-side syscall implementation


uint64
sys_hello(void)
{
struct proc *p = myproc();
// A kernel-side message — printed with cprintf (runs in kernel)
printf("kernel: hello() called by pid %d (running in kernel)\n", p->pid);

// Optionally perform other kernel-only actions here, e.g.:


// - allocate kernel memory
// - inspect process fields
// Keep it short and safe.

return 0; // return value visible to the user caller


}

Notes:

• printf prints from kernel (visible in QEMU console).


• myproc() returns the current proc pointer (ensure no duplicate struct proc
definitions exist; if earlier edits caused duplicates, remove them so proc.h is the
single definition).

kernel/syscall.c — wire the syscall into the table

Open kernel/syscall.c. At the top, add an extern if needed (some xv6 variants don't require
it, but it's safe):
extern uint64 sys_hello(void);

Then in the syscalls[] array mapping (look for a list like [SYS_fork] sys_fork, ...), add:

[SYS_hello] sys_hello,

Put it at index SYS_hello (the array is keyed by syscall number macro, so ordering in the
source is flexible as long as you use the macro).

Write a program to call syscall

Edit your user hello program (file user/hello.c) with this code. This program calls the syscall
wrapper hello() which user/usys.pl will generate into user/usys.S:

// user/hello.c
// Call kernel hello syscall.

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user.h"

int
main(void)
{
// call kernel-side hello
hello();

// We can also print from user side if desired


printf("user: hello() returned, back in user mode\n");
exit(0);
}
4) Rebuild the project
Run these commands from the xv6 top-level directory:

# regenerate syscall stubs, rebuild, and run


make clean
make user/usys.S # explicitly regenerate user syscall stubs
make
make qemu

make user/usys.S runs user/usys.pl and generates user/usys.S. You should see that usys.S
contains a stub for hello.

Start xv6 (if not already started with make qemu) and run:

$ hello

You should see in the QEMU console the kernel message printed by cprintf, something like:

kernel: hello() called by pid 2 (running in kernel)


user: hello() returned, back in user mode

kernel: lines come from cprintf (kernel). The user: line is printed after the syscall returns to
userland.

5) Troubleshooting checklist
If you see build or runtime errors, check the following:

1. Duplicate struct proc — If you previously edited kernel headers and accidentally
duplicated struct proc, remove the duplicate so kernel/proc.h is the single
definition. Use:
grep -nR -- "struct proc {" kernel || true

There should be only one hit (kernel/proc.h). If there are more, delete the accidental copy.

2. Missing user/usys.S or missing sleep earlier problems — Ensure user/usys.pl


contains entry("hello"); and re-run make user/usys.S. Confirm the generated file
contains hello:

grep -n "hello" user/usys.S || true

3. Syscall number conflict — Make sure SYS_hello is assigned the next unused
number. If you accidentally used an existing number, the wrong syscall will be
invoked.
4. Compile errors about cprintf or myproc — Ensure kernel/sysproc.c includes
defs.h and proc.h as in the snippet above.
5. Linker undefined reference to hello when linking user binary — That means
user/usys.S did not generate the stub. Re-run make user/usys.S and verify.
6. Kernel messages not appearing — In QEMU, kernel cprintf output appears in the
QEMU console window. If you launched QEMU in a way that doesn't show the
console, run make qemu in a terminal.

6) Your exercise
Write another program to:

1. Allocate memory in kernel


2. List process table entries

You might also like