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翻译虚拟地址到物理地址使用了三层。每层page table存储512个PTE,分别使用9个bit来索引。上一层的一个PTE的PPN对应下一层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, 以此类推。 只有最后一级pte会设置除了PTE_V以外的其他位,其他层级的pte只设置PTE_V。 3.2 Kernel address space // TODO: replace this image 上图PHYSTOP为0x88000000, 见memlayout.h QEMU模拟RAM从0x80000000物理地址开始,至多到0x80000000+128*1024*1024=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....

2024-01-11 · 2 min

xv6_chapter2 Operating system organization

2.1 Abstracting physical resources 2.2 User mode, supervisor mode, and system calls 2.3 Kernel Organization monolithic kernel vs microkernel xv6 和 Unix-like OS 属于 monolithic kernel. 2.4 Code: xv6 organization kernel 接口都在 def.h 中声明. 2.5 Process overview 进程的地址空间: RISC-V 64 只使用 64 位地址中的 39 位, 所以用户空间和内核空间各占 2^39 字节. xv6 只使用 39 位地址中的 38 位, 所以最大地址为 2^38 - 1 = 0x3fffffffff, MAXVA 在 riscv.h 中定义. 每个进程都有两个栈, 一个是内核栈, 一个是用户栈. 2.6 Code: starting xv6, the first process and system call bootloader 把 xv6 kernel code 加载到内存 0x80000000 地址, 首先执行的代码为entry....

2024-01-04 · 2 min

xv6_lab2 System calls

Trace 实现系统调用int trace(int mask);, 当调用mask中包含的系统调用号,打印出来。 在Makefile中增加trace用户程序的编译 UPROGS=\ ... $U/_trace 在user/user.h中增加函数声明 int trace(int); 在usys.pl中增加user space trace函数的入口。可以看到user space调用的系统调用, 是由这个脚本生成的函数。 以trace为例, 提供了trace函数的入口.global trace, 随后将定义在syscall.h中的SYS_trace编号存入寄存器a7, 通过ecall命令进入内核态。 sub entry { my $name = shift; print ".global $name\n"; print "${name}:\n"; print " li a7, SYS_${name}\n"; print " ecall\n"; print " ret\n"; } entry("trace"); 在syscall.c的syscall函数中通过获取a7寄存器中的编号,找到我们添加的系统调用函数,sys_trace。 函数具体实现如下: // 在proc结构体中增加mask成员 struct proc { //... int mask; } // 将user space传入的mask,传递给当前进程的mask变量 uint64 sys_trace(void) { if(argint(0, &myproc()->mask) < 0) return -1; return 0; } // 随后执行的系统调用number如果 (1 << num == mask), 则打印 syscall(void) { // ....

2024-01-04 · 1 min

xv6_chapter1 Operating system interfaces

XV6 实现的所有系统调用: int fork() //Create a process, return child’s PID. void exit(int status) //Terminate the current process; status reported to wait(). No return. int wait(int *status) //Wait for a child to exit; exit status in *status; returns child PID. int kill(int pid) //Terminate process PID. Returns 0, or -1 for error. int getpid() //Return the current process’s PID. int sleep(int n) //Pause for n clock ticks. int exec(char *file, char *argv[]) //Load a file and execute it with arguments; only returns if error....

2023-12-30 · 3 min

Zephyr -- Pinctrl Subsystem

Device Tree Pinctrl controller节点: 所有可选的支持属性可以查阅/dts/bindings/pinctrl/pincfg-node.yaml,支持配置上下拉,驱动能力等等。具体支持属性要参考soc的yaml文件/dts/bindings/pinctrl/xxx.yaml 当设备节点调用perip0_default的时候,group1~N都会被apply。 /* board-pinctrl.dtsi */ #include <vnd-soc-pkgxx.h> &pinctrl { /* Node with pin configuration for default state */ periph0_default: periph0_default { group1 { /* Mappings: PERIPH0_SIGA -> PX0, PERIPH0_SIGC -> PZ1 */ pinmux = <PERIPH0_SIGA_PX0>, <PERIPH0_SIGC_PZ1>; /* Pins PX0 and PZ1 have pull-up enabled */ bias-pull-up; }; ... groupN { /* Mappings: PERIPH0_SIGB -> PY7 */ pinmux = <PERIPH0_SIGB_PY7>; }; }; }; 使用pinctrl的设备节点: &uart0 { pinctrl-0 = <&uart0_default>; pinctrl-1 = <&uart0_sleep>; pinctrl-names = "default", "sleep"; }; 默认支持default, sleep两种属性,也可以自定义属性,比如slow, fast,这样需要在具体driver中自定义PINCTRL_STATE_XXX, 比如...

2023-12-25 · 3 min

Zephyr -- System Call

C Prototype 系统调用函数必须以__syscall开头,在include/或SYSCALL_INCLUDE_DIRS目录下被声明。 scripts/build/parse_syscalls.py会parse__syscall这个标记,并且函数有以下限制: 参数不能传入数组,比如int foo[] or int foo[12], 必须用int *foo代替。 函数指针不能正常解析,需要先对函数指针typedef,再传入。 定义了系统调用的xxx.h头文件必须要文件末尾加上同名的#include <syscall/xxx.h>。 Invocation Context 调用上下文 如果定义了CONFIG_USERSPACE,所有的system call APIs都会直接调用对应的implementation function。 如果定义了__ZEPHYR_SUPERVISOR__,表示所有code都在supervisor mode下运行,直接调用对应的implementation function。 如果定义了__ZEPHYR_USER__ Implementation Detail 实现细节 scripts/build/gencalls.py会把parse到的system calls放进/build/include/generated/syscall_list.h中。以K_SYSCALL_前缀加上API。 所有可以调用的system calls被保存在syscall_dispatch.c中的_k_syscall_table中。 system calls的API实现被保存在了/build/include/generated/syscalls/xxx.h 这些文件被/include/xxx.h文件最后include进去,完成函数的定义。e.g // include/i2c.h __syscall int i2c_configure(const struct device *dev, uint32_t dev_config); //声明 static inline int z_impl_i2c_configure(const struct device *dev, uint32_t dev_config) { const struct i2c_driver_api *api = (const struct i2c_driver_api *)dev->api; return api->configure(dev, dev_config); } //... #include <syscalls/i2c....

2023-12-18 · 1 min

Zephyr -- SPI framework

DeviceTree spi0: spi@f0020000 { compatible = "snps,designware-spi"; #address-cells = <1>; #size-cells = <0>; reg = <0xf0020000 0x100>; interrupts = <40 1>; pinctrl-0 = <&spi2_default>; pinctrl-1 = <&spi2_sleep>; pinctrl-names = "default", "sleep"; fifo-depth = <32>; // 必须参数 clocks = <&sysclk>; // 和下面clock-frequency二选一,优先从&sysclk中获取clock-frequency clock-frequency = <200000000>; cs-gpios = <&gpio0 18 GPIO_ACTIVE_LOW>; // 用gpio代替cs,可选参数 status = "disabled"; slow@0 { // spi设备 compatible = "test-spi-loopback-slow"; reg = <0>; spi-max-frequency = <500000>; }; fast@0 { // @x 是指第几根片选 compatible = "test-spi-loopback-fast"; reg = <0>; // 0表示spi master controller,1表示spi slave controller spi-max-frequency = <1000000>; }; }; Consumer(应用层和其他driver如何调用spi接口) 方法1:...

2023-12-15 · 3 min

Zephyr -- I2C framework

Device Tree Consumer(应用层和其他driver如何调用i2c接口) 方法1: 设备树API // 获取i2c device node id #define I2C_DEV DT_COMPAT_GET_ANY_STATUS_OKAY(test_i2c) // i2c deivce的compatible,不是i2c bus // 通过I2C_DT_SPEC_GET宏从设备树获得i2c_dt_spec结构体 struct i2c_dt_spec { const struct device *bus; uint16_t addr; }; #define I2C_DT_SPEC_GET_ON_I2C(node_id) \ .bus = DEVICE_DT_GET(DT_BUS(node_id)), \ .addr = DT_REG_ADDR(node_id) static struct i2c_dt_spec i2c = I2C_DT_SPEC_GET(I2C_DEV); 可以调用i2c_xxx_dt进行i2c传输了 static inline int i2c_write_dt(const struct i2c_dt_spec *spec, const uint8_t *buf, uint32_t num_bytes) static inline int i2c_read_dt(const struct i2c_dt_spec *spec, uint8_t *buf, uint32_t num_bytes) static inline int i2c_write_read_dt(const struct i2c_dt_spec *spec, const void *write_buf, size_t num_write, void *read_buf, size_t num_read) 方法2:...

2023-12-15 · 3 min

Vim, Tmux, and Vim plugins

VIM Basic Command-line :e {name of file} open file for editing :ls show open buffers :help {topic} open help :help :w opens help for the :w command :help w opens help for the w movement Movement Movements in Vim are also called “nouns”. Basic movement: hjkl (left, down, up, right) Words: w (next word), b (beginning of word), e (end of word) Lines: 0 (beginning of line), ^ (first non-blank character), $ (end of line) Screen: H (top of screen), M (middle of screen), L (bottom of screen) Scroll: Ctrl-u (up), Ctrl-d (down) File: gg (beginning of file), G (end of file) Line numbers: :{number}<CR> or {number}G (line {number}) Misc: % (corresponding item) Find: f{character}, t{character}, F{character}, T{character} find/to forward/backward {character} on the current line , / ; for navigating matches Search: /{regex}, n / N for navigating matches Edits Vim’s editing commands are also called “verbs”...

2023-12-13 · 4 min

Zephyr -- Device Tree

Introduction to devicetree All Zephyr and application source code files can include and use devicetree.h. This includes device drivers, applications, tests, the kernel, etc. i2c从设备的reg地址是i2c slave address. spi从设备的reg地址是chip select. Writing property values devicetree_unfixed.h, devicetree_fixups.h: deivcetree编译出来的一些宏供C使用。 zephyr3.5只有devicetree_generated.h Devicetree bindings Binding文件用来规范设备树每个节点的表达。 The build system uses bindings to generate C macros for devicetree properties that appear in DTS files. 如果binding文件中没有列出来,不会生成C宏。 语法参考 # A high level description of the device the binding applies to: description: | This is the Vendomatic company's foo-device....

2023-12-08 · 3 min