CSAPP - Cache Lab

实验说明 在 cache lab 实验中,包含两个部分: 第一部分要求我们写一个 C program 来模拟 cache memory。 第二部分要求我们通过减少 cache miss 来优化一个矩阵转置函数。 对应的文件分别为csim.c, trans.c 可通过make和make clean来编译和删除编译文件。 PartA Writing a Cache Simulator 准备工作 我们利用valgrind生成的 trace files 作为我们 cache simulator 的 input。 格式为[space]operation address, size: I 0400d7d4,8 M 0421c7f0,4 L 04f6b868,8 S 7ff0005c8,8 I 加载指令 L 加载数据 S 存储数据 M 内存数据修改(先加载数据,再存储数据) I 前面不跟空格,L/S/M 前面需要加一个空格。 csim-ref是一个参考的 cache simulator 实现。使用LRU替换算法。 用法: Usage: ./csim-ref [-hv] -s <s> -E <E> -b <b> -t <tracefile>...

2024-08-05 · 3 min

CSAPP - Attack Lab

文件说明 ctarget: An executable program vulnerable to code-injection(CI) attacks. rtarget: An executable program vulnerable to return-oriented-programming(ROP) attacks. cookie.txt: An 8-digit hex code that you will use as a unique identifier in your attack farm.c: 用来生成 ROP 攻击的代码。 hex2raw: 生成攻击字符串的工具。 输入ctarget -h查看帮助信息: Usage: [-hq] ./ctarget -i <infile> -h Print help information -q Don't submit result to server -i <infile> Input file 我们在本地跑的话必须加上-q选项,否则会提示Running on an illegal host错误。 工具使用 hex2raw hex2raw 可以将文本中保存的 ascii 码值转化成字符串。注意每个 ascii 需要以空格分开,可以加注释(注释/* */前后需要空格)。...

2024-07-05 · 10 min

CSAPP - Data Lab

Build ./dlc bits.c: 用来检查是否符合coding guidelines. ./dlc -e bits.c: 显示每个函数用了多少个操作符。 make btest: 编译btest。 make clean Rules Each "Expr" is an expression using ONLY the following: 1. Integer constants 0 through 255 (0xFF), inclusive. You are not allowed to use big constants such as 0xffffffff. 2. Function arguments and local variables (no global variables). 3. Unary integer operations ! ~ 4. Binary integer operations & ^ | + << >> You are expressly forbidden to: 1....

2024-06-25 · 1 min

CSAPP - Bomb Lab

Setup 新建solution.txt保存答案, 运行./bomb solution.txt进行测试。 gdb bomb (gdb) r solution.txt 注意如果solution.txt 每行的结尾需要是LF结尾,否则无法正确解析。 objdump -t bomb > bomb_symbol_table.txt: 生成符号表。 objdump -d bomb > bomb.txt: 生成反汇编文件。 Answer Bomb1 首先根据bomb.txt找到bomb的执行入口main函数。 400e32: e8 67 06 00 00 call 40149e <read_line> # 读取输入的一行 400e37: 48 89 c7 mov %rax,%rdi # 把读取的字符串地址传入%rdi 400e3a: e8 a1 00 00 00 call 400ee0 <phase_1> # 调用phase_1,传入的第一个参数为%rdi 400e3f: e8 80 07 00 00 call 4015c4 <phase_defused> 400e44: bf a8 23 40 00 mov $0x4023a8,%edi 400e49: e8 c2 fc ff ff call 400b10 <puts@plt> 400e4e: e8 4b 06 00 00 call 40149e <read_line> 400e53: 48 89 c7 mov %rax,%rdi 400e56: e8 a1 00 00 00 call 400efc <phase_2> 400e5b: e8 64 07 00 00 call 4015c4 <phase_defused> 400e60: bf ed 22 40 00 mov $0x4022ed,%edi 400e65: e8 a6 fc ff ff call 400b10 <puts@plt> //....

2024-06-13 · 11 min

xv6_lab3 trap

Q1 RISC-V assembly C 代码: int g(int x) { return x+3; } int f(int x) { return g(x); } void main(void) { printf("%d %d\n", f(8)+1, 13); exit(0); } 生成的汇编代码: 000000000000001c <main>: void main(void) { 1c: 1141 addi sp,sp,-16 // 分配栈空间 1e: e406 sd ra,8(sp) // 保存main的返回地址,因为接下来要调用printf 20: e022 sd s0,0(sp) // 保存前一个函数的frame pointer 22: 0800 addi s0,sp,16 // 现在frame pointer要增加16Bytes printf("%d %d\n", f(8)+1, 13); 24: 4635 li a2,13 26: 45b1 li a1,12 28: 00000517 auipc a0,0x0 2c: 7a050513 addi a0,a0,1952 # 7c8 <malloc+0xe8> // "%d %d\n"字符串地址 30: 00000097 auipc ra,0x0 // ra=pc=0x30 34: 5f8080e7 jalr 1528(ra) # 628 <printf> // 0x30 + 0x5f8 = 0x628 exit(0); 38: 4501 li a0,0 // exit的参数,传入0 3a: 00000097 auipc ra,0x0 3e: 274080e7 jalr 628(ra) # 2ae <exit> 1....

2024-06-04 · 2 min

xv6_chapter4 Traps

Lecture gdb调试shell write函数的syscall过程: (gdb) b *0xdec # 在0xde8地址设置断点 (gdb) c (gdb) delete 1 # 删除断点 (gdb) print $pc $1 = (void (*)()) 0xdec (gdb) info r (gdb) x/3i 0xde8 # 打印0xdfe开始的三条指令 0xdfe: li a7,16 0xe00: ecall 0xe04: ret (gdb) p/x $stvec $2 = 0x3ffffff000 # user space virtual address顶部一个page,trampoline page对应kernel trap handler. (gdb) stepi warning Book Traps: system call. 通过ecall进入kernel exception. 除0,invalid virtual address. interrupt. 进入kernel device driver. 根据处理代码不同,可分为三种traps: traps from user space traps from kernel space timer interrupts 4....

2024-02-26 · 1 min

xv6 calling conventions and stack frames RISC-V

caller: not preserved across fn call. 需要调用函数来保存寄存器。参考下面例子中的ra寄存器值。 callee: preserved across fn call. 被调用函数来保存寄存器。 根据CSAPP 3.7.1栈结构看,return address属于前一栈帧保存的,所以fp register应该指向return address的地址,即当前栈帧的顶部。一个栈帧中保存在最高位地址的是previous fp。所以这里最上面一个stack frame也应该把return address去掉。 sum_to: mv t0, a0 li a0, 0 loop: add a0, a0, t0 addi t0, t0, -1 bnez t0, loop ret // 返回到li t0, 2 sum_then_double: addi sp, sp, -16; // 分配栈空间 sd ra, 0(sp) // ra的值存入sp+0地址。caller保存sum_the_double执行完的返回地址 call sum_to // 这里调用call,ra的值被设置为下一条指令地址,即li t0, 2 li t0, 2 mul a0, a0, t0 ld ra, 0(sp) // 恢复sum_them_double的返回地址 addi sp, sp, 16 ret

2024-02-25 · 1 min

xv6 Misc

xv6运行 make qemu make clean Ctrl-p 打印进程。 Ctrl-a x 退出qemu。 make grade 检查所有lab得分。 ./grade-lab-util sleep or make GRADEFLAGS=sleep grade 检查某一项作业得分。 GDB 调试 sudo aptitude install gdb-multiarch: 解决ubuntu22.04 gdb版本不匹配的问题。 一个shell运行make qemu-gdb, 另一个shell运行gdb-multiarch,会自动加载.gdbinit。如果出现如下错误: warning: File "/home/xyc/MIT-6.S081-Operation-System/.gdbinit" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load". To enable execution of this file add add-auto-load-safe-path /home/xyc/MIT-6.S081-Operation-System/.gdbinit line to your configuration file "/home/xyc/.config/gdb/gdbinit". 按照提示操作,将add-auto-load-safe-path /home/xyc/MIT-6.S081-Operation-System/.gdbinit加到/home/xyc/.config/gdb/gdbinit即可。

2024-02-01 · 1 min

xv6_lab3 pgtbl

Q1 Speed up system call 这个实验的目的是将用户程序的虚拟地址USYSCALL映射到保存有进程pid的物理地址。 这样不用通过系统调用getpid()的方式,直接通过ugetpid()访问虚拟地址就可以直接得到映射的进程pid。 #define USYSCALL (TRAPFRAME - PGSIZE) // USYSCALL位于虚拟地址顶部Trapframe下面一个page int ugetpid(void) { struct usyscall *u = (struct usyscall *)USYSCALL; // 直接访问虚拟地址 return u->pid; } 在struct proc进程结构体中增加struct usyscall *usyscall, 在分配进程函数allocproc中初始化, 分配p->pid给p->usyscall->pid: if ((p->usyscall = (struct usyscall *)kalloc()) == 0) { freeproc(p); release(&p->lock); return 0; } p->usyscall->pid = p->pid; 在给进程分配页表的函数proc_pagetable()中映射指定的虚拟地址。 注意要加上PTE_U p->pagetable = proc_pagetable(p); pagetable_t proc_pagetable(struct proc *p) { // ... // map the USYSCALL just below trapframe....

2024-02-01 · 2 min

xv6_chapter3 Page tables

3.1 Paging hardware xv6 runs on Sv39 RISC-V, 使用低39位来表示虚拟内存, 高25位没有使用。 39位中27位用作index来寻找PTE(Page table entry), 低12位表示在某个页表中的偏移地址, 正好对应4KB。每个PTE包含44bits的PPN(physical page number)和一些控制位。 实际的RISC-V CPU翻译虚拟地址到物理地址使用了三层。每层存储512个PTE,分别使用9个bit来索引。上一层的一个PTE对应下一层包含512个PTE的Page table地址。所以总共有512*512*512=2^27 PTE。每个pte占8bytes,所以需要占用的内存最多是2^30=1G, 因为没有访问到的pte是不会分配pagetable的,所以实际占用的内存会更少。 每个CPU需要把顶层的page directory物理地址加载到 satp 寄存器中, 第一个Page Directory的地址是已知的。 然后通过L2索引到第一个Page directory的PTE,读出PTE的PPN, 即第二个Page directory的起始物理地址。再根据L1索引到第二个Page directory的PTE, 以此类推。 3.2 Kernel address space 上图PHYSTOP为0x88000000, 见memlayout.h QEMU模拟RAM从0x80000000物理地址开始,至多到0x80000000+12810241024=0x88000000,xv6称这个地址为PHYSTOP。 Kernel使用RAM和device registers是直接映射的,虚拟地址和物理地址相等。 不过有一部分kernel虚拟地址不是直接映射的: Trampoline page. 在虚拟地址的最顶部。这边有意思的是物理内存中的trampoline code被映射到了两个地方,一个对应直接映射的虚拟内存中的kernel text,另一个是虚拟地址最顶部地址的一个page size。有关Trampoline page请参考第四章。 Kernel stack pages. 每个进程都有自己的kernel stack。如果访问超过了自己的kernel stack。会有guard page保护,guard page的PTE valid位置为0,导致访问异常。 3.3 Code: creating an address space TLB. 每个进程有自己的页表,切换进程时需要flush TLB, 因为之前VA-PA对应已经不成立了。通过RISC-V指令sfence.vma可以flush TLB。...

2024-01-11 · 1 min