Project 3: The trap frame and the stack frame of the very first process
Due: Oct. 13, 2022, 2:30 pm (submission: by email)
Submit a report to TA: Dohyun Kim ( email: ehgus421210@kaist.ac.kr )
Name your file with “hw3_[your studentid]”. ex: hw3_20201234.pdf
This project is to understand how to execute first user process, the initcode
. The initcode
process is created after kernel is initialized. This process is the first process in the machine. When it is created, the system was executing in the kernel mode. When creating a process in normal situation, the OS sets up the trap frame and stack frame so that after the process is created, it can go back to the user code as specified in the trap frame and the stack frame. When the process is first created, there is no place to return. The kernel needs to explicitly specify the contents of the stack frame and the trap frame so that the newly created process can be successfully switched to user mode from kernel mode. To solve this issue, xv6 construct the fake trap frame, the fake return address, and the fake context in kernel stack of initcode
process. In this project, you are required to examine the basic steps associated with executing the initcode
process and data that are saved in kernel stack of initcode
to execute it.
Steps 1. Run xv6 with gdb
Run your xv6 with gdb using below command.
$ make qemu-gdb
*** Now run 'gdb'.
qemu-system-i386 -nographic -drive file=fs.img,index=1,media=disk,format=raw -drive file=xv6.img,index=0,media=disk,format=raw -smp 2 -m 512 -S -gdb tcp::26000
Xv6 will not booted. It is because xv6 is waiting for connection from gdb. Now, Open one more terminal, change your current directory to xv6 directory. Run below command in xv6 directory.
$ gdb
+ target remote localhost:26000
The target architecture is assumed to be i8086
[f000:fff0] 0xffff0: ljmp $0x3630,$0xf000e05b
0x0000fff0 in ?? ()
+ symbol-file kernel
(gdb)
Because the .gdbinit
file in xv6 directory have initial commands to connect to xv6, it will be connected to xv6 automatically when you run just gdb
.
Steps 2. Set breakpoint
We should examine how to execute the initcode
process. The initcode process is created in userinit()
function and is executed when swtch
function is called. Set breakpoint at swtch using below command.
(gdb) br swtch
Breakpoint 1 at 0x8010469b: file swtch.S, line 11.
(gdb)
Now, let kernel continue its execution using below command.
(gdb) continue
Continuing.
[Switching to Thread 2]
The target architecture is assumed to be i386
=> 0x8010469b <swtch>: mov 0x4(%esp),%eax
Thread 1 hit Breakpoint 1, swtch () at swtch.S:11
11 movl 4(%esp), %eax
(gdb)
The swtch
function is used to context switch. It pushes context of thread that is running now, old thread, from the kernel stack of the thread. And it switch kernel stack from old thread’s to new thread’s. Finally, it pops context of new thread from its kernel stack.
We should examine the stack of new thread, initcode thread. Run below command, see assemble code of swtch
.
(gdb) disassemble
Dump of assembler code for function swtch:
=> 0x8010469b <+0>: mov 0x4(%esp),%eax
0x8010469f <+4>: mov 0x8(%esp),%edx
0x801046a3 <+8>: push %ebp
0x801046a4 <+9>: push %ebx
0x801046a5 <+10>: push %esi
0x801046a6 <+11>: push %edi
0x801046a7 <+12>: mov %esp,(%eax)
0x801046a9 <+14>: mov %edx,%esp
0x801046ab <+16>: pop %edi
0x801046ac <+17>: pop %esi
0x801046ad <+18>: pop %ebx
0x801046ae <+19>: pop %ebp
0x801046af <+20>: ret
End of assembler dump.
(gdb)
Steps 3. Find the code for stack switching and dump stack
See the code at 0x801046a9 <+14>
. This code is saving address of new threads’s stack. That is, after execution of this code, stack switching is finished. Set breakpoint at next instruction of this code using below command and continue it.
(gdb) br *0x801046ab
Breakpoint 2 at 0x801046ab: file swtch.S, line 25.
(gdb) c
Continuing.
=> 0x801046ab <swtch+16>: pop %edi
Thread 1 hit Breakpoint 2, swtch () at swtch.S:25
25 popl %edi
(gdb)
Now, print dump of stack using below command.
(gdb) x/25x $esp
0x8dffff9c: 0x00000000 0x00000000 0x00000000 0x00000000
0x8dffffac: 0x801036e0 0x80105652 0x00000000 0x00000000
0x8dffffbc: 0x00000000 0x00000000 0x00000000 0x00000000
0x8dffffcc: 0x00000000 0x00000000 0x00000000 0x00000000
0x8dffffdc: 0x00000023 0x00000023 0x00000000 0x00000000
0x8dffffec: 0x00000000 0x0000001b 0x00000200 0x00001000
0x8dfffffc: 0x00000023
There is so many values to execute initcode process such as fake trap frame, fake return address, fake context.
To Do
Explain the meaning of each values in kernel stack of initcode. Which is fake trap frame? Which is the fake context? Which is the fake return address? What is the purpose of these fake data structure? What is the core member of the data structure that affects thread of execution?
Hints
You can see the function that create initcode process.
1. See the allocproc()
It makes a space for trap frame, save return address, save context.
2. See the userinit()
It fills the trap frame in the stack.