Lab traps

RISC-V assembly

Q1

​​​​Which registers contain arguments to functions? 
​​​​For example, which register holds 13 in main's call to printf?

user/call.asm

void main(void) { 1c: 1141 addi sp,sp,-16 1e: e406 sd ra,8(sp) 20: e022 sd s0,0(sp) 22: 0800 addi s0,sp,16 printf("%d %d\n", f(8)+1, 13); 24: 4635 li a2,13 26: 45b1 li a1,12 28: 00000517 auipc a0,0x0 2c: 7b850513 addi a0,a0,1976 # 7e0 <malloc+0xe8> 30: 00000097 auipc ra,0x0 34: 610080e7 jalr 1552(ra) # 640 <printf> exit(0); 38: 4501 li a0,0 3a: 00000097 auipc ra,0x0 3e: 28e080e7 jalr 654(ra) # 2c8 <exit>

RISC-V 使用 a0-a7 暫存器傳遞參數

第 7 行 可以看到 13 儲存在 a2 暫存器

Q2

​​​​Where is the call to function f in the assembly code for main? 
​​​​Where is the call to g? 
​​​​(Hint: the compiler may inline functions.)
int g(int x) { 0: 1141 addi sp,sp,-16 2: e422 sd s0,8(sp) 4: 0800 addi s0,sp,16 return x+3; } 6: 250d addiw a0,a0,3 8: 6422 ld s0,8(sp) a: 0141 addi sp,sp,16 c: 8082 ret 000000000000000e <f>: int f(int x) { e: 1141 addi sp,sp,-16 10: e422 sd s0,8(sp) 12: 0800 addi s0,sp,16 return g(x); } 14: 250d addiw a0,a0,3 16: 6422 ld s0,8(sp) 18: 0141 addi sp,sp,16 1a: 8082 ret

從上面及 main function 可以發現,並沒有呼叫 f() 及 g(),compiler 直接算出結果

Q3

​​​​At what address is the function printf located?
0000000000000640 <printf>:

void
printf(const char *fmt, ...)
{
 640:	711d                	addi	sp,sp,-96
 642:	ec06                	sd	ra,24(sp)
 644:	e822                	sd	s0,16(sp)
 646:	1000                	addi	s0,sp,32
 648:	e40c                	sd	a1,8(s0)
 64a:	e810                	sd	a2,16(s0)
 64c:	ec14                	sd	a3,24(s0)
 64e:	f018                	sd	a4,32(s0)
 650:	f41c                	sd	a5,40(s0)
 652:	03043823          	sd	a6,48(s0)
 656:	03143c23          	sd	a7,56(s0)
  va_list ap;

  va_start(ap, fmt);
 65a:	00840613          	addi	a2,s0,8
 65e:	fec43423          	sd	a2,-24(s0)
  vprintf(1, fmt, ap);
 662:	85aa                	mv	a1,a0
 664:	4505                	li	a0,1
 666:	00000097          	auipc	ra,0x0
 66a:	de0080e7          	jalr	-544(ra) # 446 <vprintf>
}

0x640

Q4

​​​​What value is in the register ra just after the jalr to printf in main?
  30:	00000097          	auipc	ra,0x0
  34:	610080e7          	jalr	1552(ra) # 640 <printf>
  exit(0);
​​​​auipc ra, 0x0 # ra = pc + 0x0

​​​​jalr 1552(ra) # ra = pc + 4 
​​​​              # pc = 1552 + ra = 1552 + 0x30 = 1660 = 0x640

Q5

​​​​Run the following code.

​​​​unsigned int i = 0x00646c72;
​printf("H%x Wo%s", 57616, &i);
​​
​​​​What is the output? 
​​​​Here's an ASCII table that maps bytes to characters.
​​​​The output depends on that fact that the RISC-V is little-endian. 
​​​​If the RISC-V were instead big-endian what would you set i to in order to yield the same output? 
​​​​Would you need to change 57616 to a different value?

Q6

​​​​In the following code
​​​​what is going to be printed after 'y='? 
​​​​(note: the answer is not a specific value.) 
​​​​Why does this happen?

​printf("x=%d y=%d", 3);

Backtrace

diff --git a/kernel/defs.h b/kernel/defs.h index a3c962b..4b82fc2 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -80,6 +80,7 @@ int pipewrite(struct pipe*, uint64, int); void printf(char*, ...); void panic(char*) __attribute__((noreturn)); void printfinit(void); +void backtrace(void); // proc.c int cpuid(void); diff --git a/kernel/printf.c b/kernel/printf.c index 1a50203..d77c791 100644 --- a/kernel/printf.c +++ b/kernel/printf.c @@ -122,6 +122,7 @@ panic(char *s) printf("panic: "); printf(s); printf("\n"); + backtrace(); panicked = 1; // freeze uart output from other CPUs for(;;) ; @@ -133,3 +134,19 @@ printfinit(void) initlock(&pr.lock, "pr"); pr.locking = 1; } + +void +backtrace() +{ + uint64 fp, ra, end; + + fp = r_fp(); + end = PGROUNDUP(fp); + + printf("backtrace:\n"); + while (fp < end) { + ra = *((uint64*)fp - 1); + printf("%p\n", ra - 8); + fp = *((uint64*)fp - 2); + } +} diff --git a/kernel/riscv.h b/kernel/riscv.h index 20a01db..c0c7330 100644 --- a/kernel/riscv.h +++ b/kernel/riscv.h @@ -327,6 +327,14 @@ sfence_vma() asm volatile("sfence.vma zero, zero"); } +static inline uint64 +r_fp() +{ + uint64 x; + asm volatile("mv %0, s0" : "=r" (x)); + return x; +} + typedef uint64 pte_t; typedef uint64 *pagetable_t; // 512 PTEs diff --git a/kernel/sysproc.c b/kernel/sysproc.c index 3b4d5bd..bc705a3 100644 --- a/kernel/sysproc.c +++ b/kernel/sysproc.c @@ -67,6 +67,7 @@ sys_sleep(void) sleep(&ticks, &tickslock); } release(&tickslock); + backtrace(); return 0; } diff --git a/user/printf.c b/user/printf.c index 5c5c782..d2aa7e0 100644 --- a/user/printf.c +++ b/user/printf.c @@ -1,6 +1,7 @@ #include "kernel/types.h" #include "kernel/stat.h" #include "user/user.h" +#include "kernel/riscv.h" #include <stdarg.h>

Alarm

diff --git a/Makefile b/Makefile index ded5bc2..2628613 100644 --- a/Makefile +++ b/Makefile @@ -188,6 +188,7 @@ UPROGS=\ $U/_grind\ $U/_wc\ $U/_zombie\ + $U/_alarmtest\ diff --git a/answers-traps.txt b/answers-traps.txt new file mode 100644 index 0000000..1aa57ce --- /dev/null +++ b/answers-traps.txt @@ -0,0 +1,24 @@ diff --git a/kernel/proc.c b/kernel/proc.c index 959b778..e9b45b9 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -146,6 +146,12 @@ found: p->context.ra = (uint64)forkret; p->context.sp = p->kstack + PGSIZE; + // Set up alarm info. + p->alarmticks = 0; + p->alarmhandler = 0; + p->alarmtickspassed = 0; + p->alarmframe = 0; + return p; } @@ -169,6 +175,12 @@ freeproc(struct proc *p) p->killed = 0; p->xstate = 0; p->state = UNUSED; + p->alarmticks = 0; + p->alarmhandler = 0; + p->alarmtickspassed = 0; + if (p->alarmframe) + kfree((void*)p->alarmframe); + p->alarmframe = 0; } // Create a user page table for a given process, with no user memory, diff --git a/kernel/proc.h b/kernel/proc.h index d021857..97d718d 100644 --- a/kernel/proc.h +++ b/kernel/proc.h @@ -91,6 +91,8 @@ struct proc { int killed; // If non-zero, have been killed int xstate; // Exit status to be returned to parent's wait int pid; // Process ID + int alarmticks; // alarm ticks + int alarmtickspassed; // how many ticks passed. // wait_lock must be held when using this: struct proc *parent; // Parent process @@ -98,8 +100,9 @@ struct proc { // these are private to the process, so p->lock need not be held. uint64 kstack; // Virtual address of kernel stack uint64 sz; // Size of process memory (bytes) + uint64 alarmhandler; // alarm handler, called after "proc->alarmticks" ticks. pagetable_t pagetable; // User page table - struct trapframe *trapframe; // data page for trampoline.S + struct trapframe *trapframe, *alarmframe; // data page for trampoline.S struct context context; // swtch() here to run process struct file *ofile[NOFILE]; // Open files struct inode *cwd; // Current directory diff --git a/kernel/syscall.c b/kernel/syscall.c index ed65409..751af4f 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -101,6 +101,8 @@ extern uint64 sys_unlink(void); extern uint64 sys_link(void); extern uint64 sys_mkdir(void); extern uint64 sys_close(void); +extern uint64 sys_sigalarm(void); +extern uint64 sys_sigreturn(void); // An array mapping syscall numbers from syscall.h // to the function that handles the system call. @@ -126,6 +128,8 @@ static uint64 (*syscalls[])(void) = { [SYS_link] sys_link, [SYS_mkdir] sys_mkdir, [SYS_close] sys_close, +[SYS_sigalarm] sys_sigalarm, +[SYS_sigreturn] sys_sigreturn, }; void diff --git a/kernel/syscall.h b/kernel/syscall.h index bc5f356..382d781 100644 --- a/kernel/syscall.h +++ b/kernel/syscall.h @@ -20,3 +20,5 @@ #define SYS_link 19 #define SYS_mkdir 20 #define SYS_close 21 +#define SYS_sigalarm 22 +#define SYS_sigreturn 23 diff --git a/kernel/sysproc.c b/kernel/sysproc.c index bc705a3..ec51b23 100644 --- a/kernel/sysproc.c +++ b/kernel/sysproc.c @@ -92,3 +92,50 @@ sys_uptime(void) release(&tickslock); return xticks; } + +uint64 +sys_sigalarm(void) +{ + int ticks; + uint64 handler; + + // Get ticks & handler + argint(0, &ticks); + argaddr(1, &handler); + + + struct proc *proc = myproc(); + + if (ticks == 0 && handler == 0) { + proc->alarmhandler = 0; + proc->alarmticks = 0; + proc->alarmtickspassed = 0; + return 0; + } + // store data to proc + proc->alarmticks = ticks; + proc->alarmhandler = handler; + char *p = kalloc(); + if (!p) + panic("sigalarm"); + proc->alarmframe = (struct trapframe *)(uint64)p; + // save previous proc state + *proc->alarmframe = *proc->trapframe; + + return 0; +} + +uint64 +sys_sigreturn(void) +{ + struct proc *proc = myproc(); + *proc->trapframe = *proc->alarmframe; + proc->alarmtickspassed = 0; + + if (proc->alarmticks == 0) { + if (proc->alarmframe) + kfree(proc->alarmframe); + proc->alarmframe = 0; + } + return proc->trapframe->a0; +} \ No newline at end of file diff --git a/kernel/trap.c b/kernel/trap.c index 512c850..0ae7480 100644 --- a/kernel/trap.c +++ b/kernel/trap.c @@ -77,8 +77,18 @@ usertrap(void) exit(-1); // give up the CPU if this is a timer interrupt. - if(which_dev == 2) - yield(); + if(which_dev == 2) { + if (p->alarmticks != 0) { + if (p->alarmticks != p->alarmtickspassed) { + p->alarmtickspassed += 1; + if (p->alarmticks == p->alarmtickspassed) { + *p->alarmframe = *p->trapframe; + p->trapframe->epc = p->alarmhandler; + } + } + } + yield(); + } usertrapret(); } diff --git a/user/user.h b/user/user.h index 4d398d5..9426153 100644 --- a/user/user.h +++ b/user/user.h @@ -22,6 +22,8 @@ int getpid(void); char* sbrk(int); int sleep(int); int uptime(void); +int sigalarm(int ticks, void (*handler)()); +int sigreturn(void); // ulib.c int stat(const char*, struct stat*); diff --git a/user/usys.pl b/user/usys.pl index 01e426e..fa548b0 100755 --- a/user/usys.pl +++ b/user/usys.pl @@ -36,3 +36,5 @@ entry("getpid"); entry("sbrk"); entry("sleep"); entry("uptime"); +entry("sigalarm"); +entry("sigreturn");