RISC-V assembly
- Which registers contain arguments to functions? For example, which register holds 13 in main's call to printf?
a0-a7 a2 - 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.)
所有都内连了,没有调用。相当于函数直接嵌入了。 - At what address is the function printf located?
0000000000000640
63a: 6442 ld s0,16(sp)
63c: 6161 addi sp,sp,80
63e: 8082 ret
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
-
What value is in the register ra just after the jalr to printf in main?
38 -
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?
Here's a description of little- and big-endian and a more whimsical description.
He110 World
需要,因为大端存储将高字节放在低地址,这样打印字符串就是64(d) 6c(l) 72(r)的顺序,所以需要反过来存,一次读取一个字节也就是8位 726c64。
第一个输出的只是16进制,所以换大小端读取的数据是相同的,不需要更换。
6. 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);
这里会读取a0,a1寄存器的值,只给了a0的值。输出a1的值就是之前的值
Backtrace
- Xv6 allocates one page for each stack in the xv6 kernel at PAGE-aligned address. You can compute the top and bottom address of the stack page by using PGROUNDDOWN(fp) and PGROUNDUP(fp) (see kernel/riscv.h. These number are helpful for backtrace to terminate its loop.
xv6会给每一个栈分配一页,所以就在这一页去找依次的调用。注意栈是从高往低增长,所以要回溯,最后的起点一定是页的第一个地址,所以使用PGROUNDUP得到起始地址。
![[Pasted image 20230201110540.png]]
- kernel/defs.h增加backtrace的声明
void backtrace(void));
- kernel/riscv.h增加寻找当前栈指针的函数
static inline uint64
r_fp()
{
uint64 x;
asm volatile("mv %0, s0" : "=r" (x) );
return x;
}
- 实现backtrace函数
// 这里需要注意的是打印和更新的都是fp指向地址内的值,所以需要解引用
void
backtrace(void)
{
uint64 fp = r_fp();
while (fp != PGROUNDUP(fp))
{
printf("%p\n", *(uint64*)(fp - 8));
fp = *(uint64*)(fp - 16);
}
return;
}
Alarm
增加一个在进程使用cpu时定期警报的功能。
- 增加sigalarm(n, fn)系统调用 系统时钟每n次后,调用fn。在user/alarmtest.c中有user调用。
- alarmtest 调用 sigalarm(2, periodic)
- alarmtest usertests 进行验证
- 给sigalarm和sigreturn添加系统调用(这里第二个lab有,参照加上就可以)
- struct proc增加参数,从而支撑下面的行为
struct proc{
...
//增加
int alarm_interval;
uint64 handler_va;
struct trapframe saved_trapframe;
int passed_ticks;
int have_return;
}
- 实现sigalarm
sigalarm功能:
- 拿到interval
- 拿到handler的地址
- 增加have_return从而标志当前是否已经有alarm了,防止时间太短,handler没有处理完,然后tick到了又进handler了。
uint64
sys_sigalarm(void)
{
int alarm_interval;
uint64 handler_va;
argint(0, &alarm_interval);
argaddr(1, &handler_va);
struct proc *p = myproc();
p->alarm_interval = alarm_interval;
p->handler_va = handler_va;
p->have_return = 1;
return p->trapframe->a0;
}
- 实现sigreturn (handler调用,从而返回调用的函数)
- 恢复trapframe,因为切换到handler函数后,再回到内核可能会更改trapframe的值
- 恢复have_return 从而可以让下一次进行
- 更改trap.c中usertrap()
使得每次时钟周期都会进行相应的操作
if(which_dev == 2)
//增加
{
if (p->alarm_interval && p->have_return)
{
if (++p->passed_ticks == p->alarm_interval)
{
p->saved_trapframe = *p->trapframe;
p->trapframe->epc = p->handler_va;
p->passed_ticks = 0;
p->have_return = 0;
}
}
//增加
yield();
}
标签:fp,uint64,return,interval,xv6,trapframe,handler,lab4
From: https://www.cnblogs.com/yych0745/p/17082025.html