Quiz 2

xv6 Booting — QEMU, System Calls, Kernel Organization

697 words
3 min read
Python Week 1: the first filter for runtime behavior
Visual companion
Python
Type and operator map

Python Week 1: the first filter for runtime behavior

View
Revision summary

What this note is really saying

Short form

# xv6 Booting — QEMU, System Calls, Kernel Organization ## 🎯 Learning Objectives - Trace the xv6 boot sequence from power-on to shell - Implement a system call in xv6 - Understand xv6 kernel source organization - Explain trap handling in xv6 * * * ## 1. xv6 on QEMU ### 1.1 QEMU Emulation QEMU emulates a RISC-V virt...

xv6 Booting — QEMU, System Calls, Kernel Organization

🎯 Learning Objectives

  • Trace the xv6 boot sequence from power-on to shell
  • Implement a system call in xv6
  • Understand xv6 kernel source organization
  • Explain trap handling in xv6

1. xv6 on QEMU

1.1 QEMU Emulation

QEMU emulates a RISC-V virt machine with:
  • CPU: RV64 (RISC-V 64-bit) with 4 harts (hardware threads)
  • Memory: 128 MB of RAM at address 0x80000000
  • Devices: UART (console), PLIC (interrupt controller), VirtIO disk

1.2 Boot Sequence Detail

(Diagram)

2. System Call Implementation

2.1 Flow of a System Call

(Diagram)

2.2 Adding a New System Call

Step 1: Add syscall number in kernel/syscall.h
c
#define SYS_mysyscall 22  // Choose next available number
Step 2: Implement the function in kernel/sysproc.c
c
uint64 sys_mysyscall(void) {
    int arg;
    argint(0, &arg);  // Get first argument
    printf("mysyscall called with arg: %d\n", arg);
    return arg * 2;   // Return something useful
}
Step 3: Add entry in kernel/syscall.c
c
extern uint64 sys_mysyscall(void);
// In the syscalls array:
[SYS_mysyscall] sys_mysyscall,
Step 4: Add user-space wrapper in user/usys.pl
perl
entry("mysyscall");
Step 5: Declare in user/user.h
c
int mysyscall(int);

3. Trap Handling

3.1 Types of Traps

TypeCauseHandler
ecallSystem callusertrap()syscall()
ExceptionPage fault, illegal instructionusertrap() → kill or fixup
Timer interruptPreemptive schedulingusertrap()yield()
Device interruptUART, disk I/Odevintr() → handle device

3.2 Trap Flow

(Diagram)

4. Process Scheduling in xv6

4.1 Round Robin Scheduler

c
// kernel/proc.c
void scheduler(void) {
    struct proc *p;
    struct cpu *c = mycpu();
    c->proc = 0;
    for (;;) {
        // Avoid deadlock by enabling interrupts
        intr_on();
        for (p = proc; p < &proc[NPROC]; p++) {
            acquire(&p->lock);
            if (p->state == RUNNABLE) {
                // Switch to this process
                p->state = RUNNING;
                c->proc = p;
                swtch(&c->context, &p->context);
                // Process returned; clean up
                c->proc = 0;
            }
            release(&p->lock);
        }
    }
}

4.2 Context Switch in xv6

The swtch function saves callee-saved registers (ra, sp, s0-s11) to the current context and loads from the new context:
asm
# kernel/swtch.S
swtch:
    sd ra, 0(a0)    # Save return address
    sd sp, 8(a0)    # Save stack pointer
    sd s0, 16(a0)   # Save callee-saved regs
    ...
    ld ra, 0(a1)    # Load new return address
    ld sp, 8(a1)    # Load new stack pointer
    ld s0, 16(a1)   # Load callee-saved regs
    ...
    ret             # Jump to new context

5. 📝 Practice Questions

Q1: What is the first C function called during xv6 boot?
Answer: main() in kernel/main.c. Before reaching main(), the assembly code in entry.s sets up the initial page table and stack. Q2: How does xv6 transition from user mode to kernel mode?
Answer: The user program executes the ecall instruction, which triggers a trap. The CPU switches to supervisor mode, saves user registers to the trapframe, and jumps to the kernel's trap handler (stvec) which calls usertrap(). Q3: How does the xv6 scheduler prevent races when accessing process state?
Answer: Each process has a spinlock (p->lock) that must be held when examining or changing process state. The scheduler acquires this lock before checking p->state == RUNNABLE and releases it only after context switching to the process. Q4: What is the role of user_init() in xv6?
Answer: user_init() creates the very first user process. It allocates a process slot, loads the initcode binary (a tiny program that execs /init), sets up the user address space, and marks the process as RUNNABLE. Q5: Why does the xv6 scheduler enable interrupts at the start of each iteration?
Answer: To ensure that timer interrupts can occur. If interrupts were disabled, the timer interrupt would never fire, and processes would never be preempted. The scheduler briefly enables interrupts to allow pending interrupts to be delivered.

6. 🔗 Cross-References

Document outline

Keep your place and jump directly to a heading.

Table of Contents
System Normal // Awaiting Context

Intelligence Hub

Navigate the knowledge graph to generate context. The Hub adapts dynamically to surface backlinks, related notes, and metadata insights.