Musings from the mountains himwant.org

xv6 memory management

+555 -6
+3 -2
src/content/posts/xv6-intro/index.md
··· 1 1 --- 2 2 title: "xv6 - Introduction" 3 - published: 2025-12-17 3 + published: 2026-01-02 4 4 draft: false 5 - description: 'Documenting the xv6 kernel' 5 + description: 'Introducing the xv6 kernel' 6 + series: 'xv6' 6 7 tags: ["xv6","os"] 7 8 --- 8 9
+2 -1
src/content/posts/xv6-intro/index.org
··· 1 1 #+title: xv6 - Introduction 2 2 #+author: Akshit Gaur 3 - #+description: Documenting the xv6 kernel 3 + #+description: Introducing the xv6 kernel 4 4 #+TAGS: xv6, os 5 + #+SERIES: xv6 5 6 #+OPTIONS: toc:nil 6 7 7 8 * Why??
+274
src/content/posts/xv6-mem-management/index.md
··· 1 + --- 2 + title: "xv6 - Memory Management" 3 + published: 2026-01-02 4 + draft: false 5 + description: 'Documenting the xv6 kernel' 6 + series: 'xv6' 7 + tags: ["xv6","os"] 8 + --- 9 + 10 + We know that xv6 uses a simple freelist, keeping that in mind, let's take a look at [kalloc.c](https://github.com/mit-pdos/xv6-riscv/blob/riscv/kernel/kalloc.c)! 11 + 12 + - ```c 13 + extern char end[]; 14 + ``` 15 + 16 + First memory address after kernel. 17 + Defined by `kernel.ld` 18 + - ```c 19 + struct run { 20 + struct run *next; 21 + }; 22 + ``` 23 + 24 + This is the freelist. 25 + - ```c 26 + struct { 27 + struct spinlock lock; 28 + struct run *freelist; 29 + } kmem; 30 + ``` 31 + 32 + Protects the freelist with a lock. 33 + - ```c 34 + void 35 + kinit() 36 + { 37 + initlock(&kmem.lock, "kmem"); 38 + freerange(end, (void*)PHYSTOP); 39 + } 40 + ``` 41 + 42 + Initialises the lock and frees the entire memory space above the kernel. 43 + - ```c 44 + void 45 + freerange(void *pa_start, void *pa_end) 46 + { 47 + char *p; 48 + p = (char*)PGROUNDUP((uint64)pa_start); 49 + for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE) 50 + kfree(p); 51 + } 52 + ``` 53 + 54 + We go from `pa_start` to `pa_end` and free each page! 55 + 56 + The next two functions do require a closer look- 57 + 58 + 59 + # kfree 60 + 61 + ```c 62 + void 63 + kfree(void *pa) 64 + { 65 + struct run *r; 66 + 67 + if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP) 68 + panic("kfree"); 69 + 70 + // Fill with junk to catch dangling refs. 71 + memset(pa, 1, PGSIZE); 72 + 73 + r = (struct run*)pa; 74 + 75 + acquire(&kmem.lock); 76 + r->next = kmem.freelist; 77 + kmem.freelist = r; 78 + release(&kmem.lock); 79 + } 80 + ``` 81 + 82 + Let's go line by line here. 83 + 84 + 1. ```c 85 + void 86 + kfree(void *pa) 87 + { 88 + struct run *r; 89 + 90 + ... 91 + } 92 + ``` 93 + 94 + - We take in physical address (`pa`) as input, which is a pointer that points to the page we want to free. 95 + - We also create `struct run` to actually manipulate memory. 96 + 2. ```c 97 + if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP) 98 + panic("kfree"); 99 + ``` 100 + 101 + Here, we define the conditions in which `kfree` is invalid to run and should panic- 102 + 103 + - `(uint64)pa % PGSIZE != 0` -> The pointer `pa` is not page-aligned! 104 + 105 + - `(char*)pa < end` -> `pa` points to a kernel page! 106 + 107 + - `(uint64)pa >= PHYSTOP` -> `pa` points to a memory address larger than the physical memory! 108 + 109 + 3. ```c 110 + memset(pa, 1, PGSIZE); 111 + ``` 112 + 113 + As is quite obvious here, we fill the entire page with junk value so a snooping program cannot see the residual data. It also helps in debugging and catching dangling refs. 114 + 115 + 4. ```c 116 + r = (struct run*)pa; 117 + 118 + acquire(&kmem.lock); 119 + r->next = kmem.freelist; 120 + kmem.freelist = r; 121 + release(&kmem.lock); 122 + ``` 123 + 124 + Step-by-step execution of this block- 125 + 126 + - We assign the page to `r`. 127 + 128 + - We acquire the lock to memory. 129 + 130 + - We prepend this page to the start of the freelist. 131 + 132 + - We point the freelist pointer to `r`. 133 + 134 + - We release the lock on memory we are currently holding. 135 + 136 + And that concludes the `kfree` function! 137 + 138 + 139 + # kalloc 140 + 141 + ```c 142 + void * 143 + kalloc(void) 144 + { 145 + struct run *r; 146 + 147 + acquire(&kmem.lock); 148 + r = kmem.freelist; 149 + if(r) 150 + kmem.freelist = r->next; 151 + release(&kmem.lock); 152 + 153 + if(r) 154 + memset((char*)r, 5, PGSIZE); // fill with junk 155 + return (void*)r; 156 + } 157 + ``` 158 + 159 + Let's again analyse each line (only unique ones)- 160 + 161 + 1. ```c 162 + struct run *r; 163 + 164 + acquire(&kmem.lock); 165 + r = kmem.freelist; 166 + ``` 167 + 168 + We acquire the memory lock and point a variable `r` to it. 169 + 170 + 2. ```c 171 + if(r) 172 + kmem.freelist = r->next; 173 + release(&kmem.lock); 174 + ``` 175 + 176 + If r exists, we move freelist pointer to the 2nd node and release the lock. 177 + 178 + 3. ```c 179 + if(r) 180 + memset((char*)r, 5, PGSIZE); // fill with junk 181 + return (void*)r; 182 + ``` 183 + 184 + Again, if r exists, we fill it with junk to foil any would be snooper's plan! 185 + 186 + And that concludes the `kalloc.c` file in xv6 kernel! You are finally ready to conquer your memory!! 187 + 188 + Our final `kalloc.c` 189 + 190 + ```c 191 + // Physical memory allocator, for user processes, 192 + // kernel stacks, page-table pages, 193 + // and pipe buffers. Allocates whole 4096-byte pages. 194 + 195 + #include "types.h" 196 + #include "param.h" 197 + #include "memlayout.h" 198 + #include "spinlock.h" 199 + #include "riscv.h" 200 + #include "defs.h" 201 + 202 + void freerange(void *pa_start, void *pa_end); 203 + 204 + extern char end[]; // first address after kernel. 205 + // defined by kernel.ld. 206 + 207 + struct run { 208 + struct run *next; 209 + }; 210 + 211 + struct { 212 + struct spinlock lock; 213 + struct run *freelist; 214 + } kmem; 215 + 216 + void 217 + kinit() 218 + { 219 + initlock(&kmem.lock, "kmem"); 220 + freerange(end, (void*)PHYSTOP); 221 + } 222 + 223 + void 224 + freerange(void *pa_start, void *pa_end) 225 + { 226 + char *p; 227 + p = (char*)PGROUNDUP((uint64)pa_start); 228 + for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE) 229 + kfree(p); 230 + } 231 + 232 + // Free the page of physical memory pointed at by pa, 233 + // which normally should have been returned by a 234 + // call to kalloc(). (The exception is when 235 + // initializing the allocator; see kinit above.) 236 + void 237 + kfree(void *pa) 238 + { 239 + struct run *r; 240 + 241 + if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP) 242 + panic("kfree"); 243 + 244 + // Fill with junk to catch dangling refs. 245 + memset(pa, 1, PGSIZE); 246 + 247 + r = (struct run*)pa; 248 + 249 + acquire(&kmem.lock); 250 + r->next = kmem.freelist; 251 + kmem.freelist = r; 252 + release(&kmem.lock); 253 + } 254 + 255 + // Allocate one 4096-byte page of physical memory. 256 + // Returns a pointer that the kernel can use. 257 + // Returns 0 if the memory cannot be allocated. 258 + void * 259 + kalloc(void) 260 + { 261 + struct run *r; 262 + 263 + acquire(&kmem.lock); 264 + r = kmem.freelist; 265 + if(r) 266 + kmem.freelist = r->next; 267 + release(&kmem.lock); 268 + 269 + if(r) 270 + memset((char*)r, 5, PGSIZE); // fill with junk 271 + return (void*)r; 272 + } 273 + ``` 274 +
+271
src/content/posts/xv6-mem-management/index.org
··· 1 + # Created 2026-01-02 Fri 19:58 2 + #+options: toc:nil 3 + #+title: xv6 - Memory Management 4 + #+author: Akshit Gaur 5 + #+description: How does xv6 manage memory? 6 + #+tags: xv6, os 7 + #+series: xv6 8 + #+property: header-args:C :noweb no-export 9 + 10 + We know that xv6 uses a simple freelist, keeping that in mind, let's take a look at [[https://github.com/mit-pdos/xv6-riscv/blob/riscv/kernel/kalloc.c][kalloc.c]]! 11 + 12 + - 13 + #+begin_src C 14 + extern char end[]; 15 + #+end_src 16 + First memory address after kernel. 17 + Defined by =kernel.ld= 18 + - 19 + #+begin_src C 20 + struct run { 21 + struct run *next; 22 + }; 23 + #+end_src 24 + 25 + This is the freelist. 26 + - 27 + #+begin_src C 28 + struct { 29 + struct spinlock lock; 30 + struct run *freelist; 31 + } kmem; 32 + #+end_src 33 + Protects the freelist with a lock. 34 + - 35 + #+begin_src C 36 + void 37 + kinit() 38 + { 39 + initlock(&kmem.lock, "kmem"); 40 + freerange(end, (void*)PHYSTOP); 41 + } 42 + #+end_src 43 + 44 + Initialises the lock and frees the entire memory space above the kernel. 45 + - 46 + #+begin_src C 47 + void 48 + freerange(void *pa_start, void *pa_end) 49 + { 50 + char *p; 51 + p = (char*)PGROUNDUP((uint64)pa_start); 52 + for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE) 53 + kfree(p); 54 + } 55 + #+end_src 56 + We go from =pa_start= to =pa_end= and free each page! 57 + 58 + 59 + The next two functions do require a closer look- 60 + * kfree 61 + #+begin_src C 62 + void 63 + kfree(void *pa) 64 + { 65 + struct run *r; 66 + 67 + if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP) 68 + panic("kfree"); 69 + 70 + // Fill with junk to catch dangling refs. 71 + memset(pa, 1, PGSIZE); 72 + 73 + r = (struct run*)pa; 74 + 75 + acquire(&kmem.lock); 76 + r->next = kmem.freelist; 77 + kmem.freelist = r; 78 + release(&kmem.lock); 79 + } 80 + #+end_src 81 + 82 + Let's go line by line here. 83 + 84 + 1. 85 + #+begin_src C 86 + void 87 + kfree(void *pa) 88 + { 89 + struct run *r; 90 + 91 + ... 92 + } 93 + #+end_src 94 + - We take in physical address (=pa=) as input, which is a pointer that points to the page we want to free. 95 + - We also create =struct run= to actually manipulate memory. 96 + 2. 97 + #+begin_src C 98 + if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP) 99 + panic("kfree"); 100 + #+end_src 101 + Here, we define the conditions in which =kfree= is invalid to run and should panic- 102 + - ~(uint64)pa % PGSIZE != 0~ -> The pointer =pa= is not page-aligned! 103 + 104 + - ~(char*)pa < end~ -> =pa= points to a kernel page! 105 + 106 + - ~(uint64)pa >= PHYSTOP~ -> =pa= points to a memory address larger than the physical memory! 107 + 108 + 3. 109 + #+begin_src C 110 + memset(pa, 1, PGSIZE); 111 + #+end_src 112 + As is quite obvious here, we fill the entire page with junk value so a snooping program cannot see the residual data. It also helps in debugging and catching dangling refs. 113 + 114 + 4. 115 + #+begin_src C 116 + r = (struct run*)pa; 117 + 118 + acquire(&kmem.lock); 119 + r->next = kmem.freelist; 120 + kmem.freelist = r; 121 + release(&kmem.lock); 122 + #+end_src 123 + Step-by-step execution of this block- 124 + - We assign the page to =r=. 125 + 126 + - We acquire the lock to memory. 127 + 128 + - We prepend this page to the start of the freelist. 129 + 130 + - We point the freelist pointer to =r=. 131 + 132 + - We release the lock on memory we are currently holding. 133 + 134 + 135 + 136 + And that concludes the =kfree= function! 137 + * [#C] kalloc 138 + #+begin_src C 139 + void * 140 + kalloc(void) 141 + { 142 + struct run *r; 143 + 144 + acquire(&kmem.lock); 145 + r = kmem.freelist; 146 + if(r) 147 + kmem.freelist = r->next; 148 + release(&kmem.lock); 149 + 150 + if(r) 151 + memset((char*)r, 5, PGSIZE); // fill with junk 152 + return (void*)r; 153 + } 154 + #+end_src 155 + 156 + Let's again analyse each line (only unique ones)- 157 + 158 + 1. 159 + #+begin_src C 160 + struct run *r; 161 + 162 + acquire(&kmem.lock); 163 + r = kmem.freelist; 164 + #+end_src 165 + We acquire the memory lock and point a variable =r= to it. 166 + 167 + 2. 168 + #+begin_src C 169 + if(r) 170 + kmem.freelist = r->next; 171 + release(&kmem.lock); 172 + #+end_src 173 + If r exists, we move freelist pointer to the 2nd node and release the lock. 174 + 175 + 3. 176 + #+begin_src C 177 + if(r) 178 + memset((char*)r, 5, PGSIZE); // fill with junk 179 + return (void*)r; 180 + #+end_src 181 + Again, if r exists, we fill it with junk to foil any would be snooper's plan! 182 + 183 + 184 + And that concludes the =kalloc.c= file in xv6 kernel! You are finally ready to conquer your memory!! 185 + 186 + 187 + Our final =kalloc.c= 188 + #+begin_src C 189 + // Physical memory allocator, for user processes, 190 + // kernel stacks, page-table pages, 191 + // and pipe buffers. Allocates whole 4096-byte pages. 192 + 193 + #include "types.h" 194 + #include "param.h" 195 + #include "memlayout.h" 196 + #include "spinlock.h" 197 + #include "riscv.h" 198 + #include "defs.h" 199 + 200 + void freerange(void *pa_start, void *pa_end); 201 + 202 + extern char end[]; // first address after kernel. 203 + // defined by kernel.ld. 204 + 205 + struct run { 206 + struct run *next; 207 + }; 208 + 209 + struct { 210 + struct spinlock lock; 211 + struct run *freelist; 212 + } kmem; 213 + 214 + void 215 + kinit() 216 + { 217 + initlock(&kmem.lock, "kmem"); 218 + freerange(end, (void*)PHYSTOP); 219 + } 220 + 221 + void 222 + freerange(void *pa_start, void *pa_end) 223 + { 224 + char *p; 225 + p = (char*)PGROUNDUP((uint64)pa_start); 226 + for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE) 227 + kfree(p); 228 + } 229 + 230 + // Free the page of physical memory pointed at by pa, 231 + // which normally should have been returned by a 232 + // call to kalloc(). (The exception is when 233 + // initializing the allocator; see kinit above.) 234 + void 235 + kfree(void *pa) 236 + { 237 + struct run *r; 238 + 239 + if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP) 240 + panic("kfree"); 241 + 242 + // Fill with junk to catch dangling refs. 243 + memset(pa, 1, PGSIZE); 244 + 245 + r = (struct run*)pa; 246 + 247 + acquire(&kmem.lock); 248 + r->next = kmem.freelist; 249 + kmem.freelist = r; 250 + release(&kmem.lock); 251 + } 252 + 253 + // Allocate one 4096-byte page of physical memory. 254 + // Returns a pointer that the kernel can use. 255 + // Returns 0 if the memory cannot be allocated. 256 + void * 257 + kalloc(void) 258 + { 259 + struct run *r; 260 + 261 + acquire(&kmem.lock); 262 + r = kmem.freelist; 263 + if(r) 264 + kmem.freelist = r->next; 265 + release(&kmem.lock); 266 + 267 + if(r) 268 + memset((char*)r, 5, PGSIZE); // fill with junk 269 + return (void*)r; 270 + } 271 + #+end_src
+3 -2
src/content/posts/xv6-spinlocking/index.md
··· 1 1 --- 2 2 title: "xv6 - SpinLocks" 3 - published: 2025-12-17 3 + published: 2026-01-02 4 4 draft: false 5 - description: 'Documenting the xv6 kernel' 5 + description: 'Should I spin or should I lock?' 6 + series: 'xv6' 6 7 tags: ["xv6","os"] 7 8 --- 8 9
+2 -1
src/content/posts/xv6-spinlocking/index.org
··· 1 1 #+title: xv6 - SpinLocks 2 2 #+author: Akshit Gaur 3 - #+description: Documenting the xv6 kernel 3 + #+description: Should I spin or should I lock? 4 4 #+TAGS: xv6, os 5 + #+SERIES: xv6 5 6 #+OPTIONS: toc:nil 6 7 #+PROPERTY: header-args:c :noweb no-export 7 8