rev |
line source |
nuclear@17
|
1 #include <stdio.h>
|
nuclear@17
|
2 #include <string.h>
|
nuclear@17
|
3 #include <inttypes.h>
|
nuclear@17
|
4 #include "vm.h"
|
nuclear@17
|
5 #include <stdio.h>
|
nuclear@17
|
6 #include "intr.h"
|
nuclear@17
|
7 #include "mem.h"
|
nuclear@17
|
8 #include "panic.h"
|
nuclear@17
|
9
|
nuclear@17
|
10
|
nuclear@17
|
11 #define KMEM_START 0xc0000000
|
nuclear@17
|
12 #define IDMAP_START 0xa0000
|
nuclear@17
|
13
|
nuclear@17
|
14 #define ATTR_PGDIR_MASK 0x3f
|
nuclear@17
|
15 #define ATTR_PGTBL_MASK 0x1ff
|
nuclear@17
|
16 #define ADDR_PGENT_MASK 0xfffff000
|
nuclear@17
|
17
|
nuclear@17
|
18 #define PAGEFAULT 14
|
nuclear@17
|
19
|
nuclear@22
|
20
|
nuclear@22
|
21 struct page_range {
|
nuclear@22
|
22 int start, end;
|
nuclear@22
|
23 struct page_range *next;
|
nuclear@22
|
24 };
|
nuclear@22
|
25
|
nuclear@22
|
26 /* defined in vm-asm.S */
|
nuclear@22
|
27 void enable_paging(void);
|
nuclear@22
|
28 void set_pgdir_addr(uint32_t addr);
|
nuclear@22
|
29 uint32_t get_fault_addr(void);
|
nuclear@22
|
30
|
nuclear@22
|
31 static void pgfault(int inum, uint32_t err);
|
nuclear@22
|
32 static struct page_range *alloc_node(void);
|
nuclear@22
|
33 static void free_node(struct page_range *node);
|
nuclear@22
|
34
|
nuclear@22
|
35 /* page directory */
|
nuclear@22
|
36 static uint32_t *pgdir;
|
nuclear@22
|
37
|
nuclear@22
|
38 /* 2 lists of free ranges, for kernel memory and user memory */
|
nuclear@22
|
39 static struct page_range *pglist[2];
|
nuclear@22
|
40 /* list of free page_range structures to be used in the lists */
|
nuclear@22
|
41 static struct page_range *node_pool;
|
nuclear@22
|
42
|
nuclear@22
|
43
|
nuclear@17
|
44 void init_vm(struct mboot_info *mb)
|
nuclear@17
|
45 {
|
nuclear@19
|
46 uint32_t idmap_end;
|
nuclear@19
|
47
|
nuclear@17
|
48 init_mem(mb);
|
nuclear@17
|
49
|
nuclear@18
|
50 pgdir = (uint32_t*)alloc_phys_page();
|
nuclear@17
|
51 memset(pgdir, 0, sizeof pgdir);
|
nuclear@17
|
52
|
nuclear@17
|
53 /* map the video memory and kernel code 1-1 */
|
nuclear@19
|
54 get_kernel_mem_range(0, &idmap_end);
|
nuclear@19
|
55 map_mem_range(IDMAP_START, idmap_end - IDMAP_START, IDMAP_START, 0);
|
nuclear@17
|
56
|
nuclear@17
|
57 interrupt(PAGEFAULT, pgfault);
|
nuclear@17
|
58
|
nuclear@17
|
59 set_pgdir_addr((int32_t)pgdir);
|
nuclear@17
|
60 enable_paging();
|
nuclear@17
|
61 }
|
nuclear@17
|
62
|
nuclear@17
|
63 void map_page(int vpage, int ppage, unsigned int attr)
|
nuclear@17
|
64 {
|
nuclear@17
|
65 uint32_t *pgtbl;
|
nuclear@17
|
66 int diridx = PAGE_TO_PGTBL(vpage);
|
nuclear@17
|
67 int pgidx = PAGE_TO_PGTBL_PG(vpage);
|
nuclear@17
|
68
|
nuclear@17
|
69 if(!(pgdir[diridx] & PG_PRESENT)) {
|
nuclear@17
|
70 uint32_t addr = alloc_phys_page();
|
nuclear@17
|
71 pgtbl = (uint32_t*)addr;
|
nuclear@18
|
72 memset(pgtbl, 0, PGSIZE);
|
nuclear@18
|
73
|
nuclear@17
|
74 pgdir[diridx] = addr | (attr & ATTR_PGDIR_MASK) | PG_PRESENT;
|
nuclear@17
|
75 } else {
|
nuclear@17
|
76 pgtbl = (uint32_t*)(pgdir[diridx] & ADDR_PGENT_MASK);
|
nuclear@17
|
77 }
|
nuclear@17
|
78
|
nuclear@17
|
79 pgtbl[pgidx] = PAGE_TO_ADDR(ppage) | (attr & ATTR_PGTBL_MASK) | PG_PRESENT;
|
nuclear@17
|
80 }
|
nuclear@17
|
81
|
nuclear@17
|
82 void unmap_page(int vpage)
|
nuclear@17
|
83 {
|
nuclear@17
|
84 uint32_t *pgtbl;
|
nuclear@17
|
85 int diridx = PAGE_TO_PGTBL(vpage);
|
nuclear@17
|
86 int pgidx = PAGE_TO_PGTBL_PG(vpage);
|
nuclear@17
|
87
|
nuclear@17
|
88 if(!(pgdir[diridx] & PG_PRESENT)) {
|
nuclear@17
|
89 goto err;
|
nuclear@17
|
90 }
|
nuclear@17
|
91 pgtbl = (uint32_t*)(pgdir[diridx] & ADDR_PGENT_MASK);
|
nuclear@17
|
92
|
nuclear@17
|
93 if(!(pgtbl[pgidx] & PG_PRESENT)) {
|
nuclear@17
|
94 goto err;
|
nuclear@17
|
95 }
|
nuclear@17
|
96 pgtbl[pgidx] = 0;
|
nuclear@17
|
97
|
nuclear@17
|
98 return;
|
nuclear@17
|
99 err:
|
nuclear@17
|
100 printf("unmap_page(%d): page already not mapped\n", vpage);
|
nuclear@17
|
101 }
|
nuclear@17
|
102
|
nuclear@22
|
103 /* if ppg_start is -1, we allocate physical pages to map with alloc_phys_page() */
|
nuclear@17
|
104 void map_page_range(int vpg_start, int pgcount, int ppg_start, unsigned int attr)
|
nuclear@17
|
105 {
|
nuclear@17
|
106 int i;
|
nuclear@17
|
107
|
nuclear@17
|
108 for(i=0; i<pgcount; i++) {
|
nuclear@22
|
109 uint32_t paddr = ppg_start == -1 ? alloc_phys_page() : ppg_start + i;
|
nuclear@22
|
110
|
nuclear@22
|
111 map_page(vpg_start + i, paddr, attr);
|
nuclear@17
|
112 }
|
nuclear@17
|
113 }
|
nuclear@17
|
114
|
nuclear@17
|
115 void map_mem_range(uint32_t vaddr, size_t sz, uint32_t paddr, unsigned int attr)
|
nuclear@17
|
116 {
|
nuclear@17
|
117 int vpg_start, ppg_start, num_pages;
|
nuclear@17
|
118
|
nuclear@17
|
119 if(!sz) return;
|
nuclear@17
|
120
|
nuclear@17
|
121 if(ADDR_TO_PGOFFS(paddr)) {
|
nuclear@17
|
122 panic("map_mem_range called with unaligned physical address: %x\n", paddr);
|
nuclear@17
|
123 }
|
nuclear@17
|
124
|
nuclear@17
|
125 vpg_start = ADDR_TO_PAGE(vaddr);
|
nuclear@17
|
126 ppg_start = ADDR_TO_PAGE(paddr);
|
nuclear@17
|
127 num_pages = ADDR_TO_PAGE(sz) + 1;
|
nuclear@17
|
128
|
nuclear@17
|
129 map_page_range(vpg_start, num_pages, ppg_start, attr);
|
nuclear@17
|
130 }
|
nuclear@17
|
131
|
nuclear@18
|
132 uint32_t virt_to_phys(uint32_t vaddr)
|
nuclear@18
|
133 {
|
nuclear@18
|
134 uint32_t pgaddr, *pgtbl;
|
nuclear@18
|
135 int diridx = ADDR_TO_PGTBL(vaddr);
|
nuclear@18
|
136 int pgidx = ADDR_TO_PGTBL_PG(vaddr);
|
nuclear@18
|
137
|
nuclear@18
|
138 if(!(pgdir[diridx] & PG_PRESENT)) {
|
nuclear@18
|
139 panic("virt_to_phys(%x): page table %d not present\n", vaddr, diridx);
|
nuclear@18
|
140 }
|
nuclear@18
|
141 pgtbl = (uint32_t*)(pgdir[diridx] & PGENT_ADDR_MASK);
|
nuclear@18
|
142
|
nuclear@18
|
143 if(!(pgtbl[pgidx] & PG_PRESENT)) {
|
nuclear@18
|
144 panic("virt_to_phys(%x): page %d not present\n", vaddr, ADDR_TO_PAGE(vaddr));
|
nuclear@18
|
145 }
|
nuclear@18
|
146 pgaddr = pgtbl[pgidx] & PGENT_ADDR_MASK;
|
nuclear@18
|
147
|
nuclear@18
|
148 return pgaddr | ADDR_TO_PGOFFS(vaddr);
|
nuclear@18
|
149 }
|
nuclear@18
|
150
|
nuclear@22
|
151 /* allocate a contiguous block of virtual memory pages along with
|
nuclear@22
|
152 * backing physical memory for them, and update the page table.
|
nuclear@22
|
153 */
|
nuclear@22
|
154 int pgalloc(int num, int area)
|
nuclear@22
|
155 {
|
nuclear@22
|
156 int ret = -1;
|
nuclear@22
|
157 struct page_range *node, *prev, dummy;
|
nuclear@22
|
158
|
nuclear@22
|
159 dummy.next = pglist[area];
|
nuclear@22
|
160 node = pglist[area];
|
nuclear@22
|
161 prev = &dummy;
|
nuclear@22
|
162
|
nuclear@22
|
163 while(node) {
|
nuclear@22
|
164 if(node->end - node->start >= num) {
|
nuclear@22
|
165 ret = node->start;
|
nuclear@22
|
166 node->start += num;
|
nuclear@22
|
167
|
nuclear@22
|
168 if(node->start == node->end) {
|
nuclear@22
|
169 prev->next = node->next;
|
nuclear@22
|
170 node->next = 0;
|
nuclear@22
|
171
|
nuclear@22
|
172 if(node == pglist[area]) {
|
nuclear@22
|
173 pglist[area] = 0;
|
nuclear@22
|
174 }
|
nuclear@22
|
175 free_node(node);
|
nuclear@22
|
176 }
|
nuclear@22
|
177 break;
|
nuclear@22
|
178 }
|
nuclear@22
|
179
|
nuclear@22
|
180 prev = node;
|
nuclear@22
|
181 node = node->next;
|
nuclear@22
|
182 }
|
nuclear@22
|
183
|
nuclear@22
|
184 if(ret >= 0) {
|
nuclear@22
|
185 /* allocate physical storage and map them */
|
nuclear@22
|
186 map_page_range(ret, num, -1, 0);
|
nuclear@22
|
187 }
|
nuclear@22
|
188
|
nuclear@22
|
189 return ret;
|
nuclear@22
|
190 }
|
nuclear@22
|
191
|
nuclear@22
|
192 void pgfree(int start, int num)
|
nuclear@22
|
193 {
|
nuclear@22
|
194 /* TODO */
|
nuclear@22
|
195 }
|
nuclear@22
|
196
|
nuclear@17
|
197 static void pgfault(int inum, uint32_t err)
|
nuclear@17
|
198 {
|
nuclear@17
|
199 printf("~~~~ PAGE FAULT ~~~~\n");
|
nuclear@17
|
200
|
nuclear@17
|
201 printf("fault address: %x\n", get_fault_addr());
|
nuclear@17
|
202
|
nuclear@17
|
203 if(err & PG_PRESENT) {
|
nuclear@17
|
204 if(err & 8) {
|
nuclear@17
|
205 printf("reserved bit set in some paging structure\n");
|
nuclear@17
|
206 } else {
|
nuclear@17
|
207 printf("%s protection violation ", (err & PG_WRITABLE) ? "write" : "read");
|
nuclear@17
|
208 printf("in %s mode\n", err & PG_USER ? "user" : "kernel");
|
nuclear@17
|
209 }
|
nuclear@17
|
210 } else {
|
nuclear@17
|
211 printf("page not present\n");
|
nuclear@17
|
212 }
|
nuclear@19
|
213
|
nuclear@19
|
214 panic("unhandled page fault\n");
|
nuclear@17
|
215 }
|
nuclear@22
|
216
|
nuclear@22
|
217 /* --- page range list node management --- */
|
nuclear@22
|
218 static struct page_range *alloc_node(void)
|
nuclear@22
|
219 {
|
nuclear@22
|
220 struct page_range *node;
|
nuclear@22
|
221 uint32_t paddr;
|
nuclear@22
|
222
|
nuclear@22
|
223 if(node_pool) {
|
nuclear@22
|
224 node = node_pool;
|
nuclear@22
|
225 node_pool = node_pool->next;
|
nuclear@22
|
226 return node;
|
nuclear@22
|
227 }
|
nuclear@22
|
228
|
nuclear@22
|
229 /* no node structures in the pool, we need to allocate and map
|
nuclear@22
|
230 * a page, split it up into node structures, add them in the pool
|
nuclear@22
|
231 * and allocate one of them.
|
nuclear@22
|
232 */
|
nuclear@22
|
233 if(!(paddr = alloc_phys_page())) {
|
nuclear@22
|
234 panic("ran out of physical memory while allocating VM range structures\n");
|
nuclear@22
|
235 }
|
nuclear@22
|
236
|
nuclear@22
|
237 /* TODO cont. */
|
nuclear@22
|
238 return 0;
|
nuclear@22
|
239 }
|
nuclear@22
|
240
|
nuclear@22
|
241 static void free_node(struct page_range *node)
|
nuclear@22
|
242 {
|
nuclear@22
|
243 node->next = node_pool;
|
nuclear@22
|
244 node_pool = node;
|
nuclear@22
|
245 }
|