1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
|
/**
** @file vm.h
**
** @author CSCI-452 class of 20245
**
** @brief Virtual memory-related declarations.
*/
#ifndef VM_H_
#define VM_H_
#include <defs.h>
#include <types.h>
#include <procs.h>
/*
** VM layout of the system
**
** User processes use the first 4MB of the 32-bit address space; see the
** next comment for details.
**
** Kernel virtual addresses are in the "higher half" range, beginning
** at 0x80000000. We define our mapping such that virtual address
** 0x8nnnnnnn maps to physical address 0x0nnnnnnn, so converting between
** the two is trivial.
*/
/*
** VM layout of process' address space
**
** Processes are limited to the first 4MB of the 32-bit address space:
**
** Address Range Contents
** ======================= ================================
** 0x00000000 - 0x00000fff page 0 is inaccessible
** 0x00001000 - 0x000..fff text occupies pages 1 - N
** 0x000..000 - 0x000..fff data occupies pages N+1 - N+d
** 0x000..000 - 0x000..fff bss occupies pages N+d+1 - N+d+b
** 0x000..000 - 0x003fdfff unusable
** 0x003fe000 - 0x003fffff stack occupies last two pages
**
** This gives us the following page table structure:
**
** Page directory:
** Entries Contents
** ======== ==============================
** 0 point to PMT for address space
** 1 - 1023 invalid
**
** Page map table:
** Entries Contents
** ======== ==============================
** 0 invalid
** 1 - N text frames
** N+1 - N+d data frames
** N+d+1 - N+d+b bss frames
** N+d+b+1 - 1021 invalid
** 1022 - 1023 stack frames
*/
/*
** General (C and/or assembly) definitions
*/
// user virtual addresses
#define USER_TEXT 0x00001000
#define USER_STACK 0x003fe000
#define USER_STK_END 0x00400000
// how to find the addresses of the stack pages in the VM hierarchy
// user address space is the first 4MB of virtual memory
#define USER_PDE 0
// the stack occupies the last two pages of the address space
#define USER_STK_PTE1 1022
#define USER_STK_PTE2 1023
// some important memory addresses
#define KERN_BASE 0x80000000 // start of "kernel" memory
#define EXT_BASE 0x00100000 // start of "extended" memory (1MB)
#define DEV_BASE 0xfe000000 // "device" memory
#define PHYS_TOP 0x3fffffff // last usable physical address (1GB - 1)
// where the kernel actually lives
#define KERN_PLINK 0x00010000
#define KERN_VLINK (KERN_BASE + KERN_PLINK)
// number of entries in a page directory or page table
#define N_PDE 1024
#define N_PTE 1024
// index field shift counts and masks
#define PDIX_SHIFT 22
#define PTIX_SHIFT 12
#define PIX2I_MASK 0x3ff
// physical/virtual converters that don't use casting
// (usable from anywhere)
#define V2PNC(a) ((a) - KERN_BASE)
#define P2VNC(a) ((a) + KERN_BASE)
// page-size address rounding macros
#define SZ_PG_M1 MOD4K_BITS
#define SZ_PG_MASK MOD4K_MASK
#define PGUP(a) (((a) + SZ_PG_M1) & SZ_PG_MASK)
#define PGDOWN(a) ((a) & SZ_PG_MASK)
// page directory entry bit fields
#define PDE_P 0x00000001 // 1 = present
#define PDE_RW 0x00000002 // 1 = writable
#define PDE_US 0x00000004 // 1 = user and system usable
#define PDE_PWT 0x00000008 // cache: 1 = write-through
#define PDE_PCD 0x00000010 // cache: 1 = disabled
#define PDE_A 0x00000020 // accessed
#define PDE_D 0x00000040 // dirty (4MB pages)
#define PDE_AVL1 0x00000040 // ignored (4KB pages)
#define PDE_PS 0x00000080 // 1 = 4MB page size
#define PDE_G 0x00000100 // global
#define PDE_AVL2 0x00000e00 // ignored
#define PDE_PAT 0x00001000 // (4MB pages) use page attribute table
#define PDE_PTA 0xfffff000 // page table address field (4KB pages)
#define PDE_FA 0xffc00000 // frame address field (4MB pages)
// page table entry bit fields
#define PTE_P 0x00000001 // present
#define PTE_RW 0x00000002 // 1 = writable
#define PTE_US 0x00000004 // 1 = user and system usable
#define PTE_PWT 0x00000008 // cache: 1 = write-through
#define PTE_PCD 0x00000010 // cache: 1 = disabled
#define PTE_A 0x00000020 // accessed
#define PTE_D 0x00000040 // dirty
#define PTE_PAT 0x00000080 // use page attribute table
#define PTE_G 0x00000100 // global
#define PTE_AVL2 0x00000e00 // ignored
#define PTE_FA 0xfffff000 // frame address field
// error code bit assignments for page faults
#define PFLT_P 0x00000001
#define PFLT_W 0x00000002
#define PFLT_US 0x00000004
#define PFLT_RSVD 0x00000008
#define PFLT_ID 0x00000010
#define PFLT_PK 0x00000020
#define PFLT_SS 0x00000040
#define PFLT_HLAT 0x00000080
#define PFLT_SGX 0x00008000
#define PFLT_UNUSED 0xffff7f00
#ifndef ASM_SRC
/*
** Start of C-only definitions
*/
// physical/virtual converters that do use casting
// (not usable from assembly)
#define V2P(a) (((uint32_t)(a)) - KERN_BASE)
#define P2V(a) (((uint32_t)(a)) + KERN_BASE)
// create a pde/pte from an integer frame number and permission bits
#define MKPDE(f, p) ((pde_t)(TO_FRAME((f)) | (p)))
#define MKPTE(f, p) ((pte_t)(TO_FRAME((f)) | (p)))
// is a PDE/PTE present?
// (P bit is in the same place in both)
#define IS_PRESENT(entry) (((entry) & PDE_P) != 0)
// is a PDE a 4MB page entry?
#define IS_LARGE(pde) (((pde) & PDE_PS) != 0)
// is this entry "system only" or "system and user"?
#define IS_SYSTEM(entry) (((entry) & PDE_US) == 0)
#define IS_USER(entry) (((entry) & PDE_US) != 0)
// low-order nine bits of PDEs and PTEs hold "permission" flag bits
#define PERMS_MASK MOD4K_MASK
// 4KB frame numbers are 20 bits wide
#define FRAME_4K_SHIFT 12
#define FRAME2I_4K_MASK 0x000fffff
#define TO_4KFRAME(n) (((n) & FRAME2I_4K_MASK) << FRAME_4K_SHIFT)
#define GET_4KFRAME(n) (((n) >> FRAME_4K_SHIFT) & FRAME2I_4K_MASK)
#define PDE_4K_ADDR(n) ((n) & MOD4K_MASK)
#define PTE_4K_ADDR(n) ((n) & MOD4K_MASK)
// 4MB frame numbers are 10 bits wide
#define FRAME_4M_SHIFT 22
#define FRAME2I_4M_MASK 0x000003ff
#define TO_4MFRAME(n) (((n) & FRAME2I_4M_MASK) << FRAME_4M_SHIFT)
#define GET_4MFRAME(n) (((n) >> FRAME_4M_SHIFT) & FRAME2I_4M_MASK)
#define PDE_4M_ADDR(n) ((n) & MOD4M_MASK)
#define PTE_4M_ADDR(n) ((n) & MOD4M_MASK)
// extract the PMT address or frame address from a table entry
// PDEs could point to 4MB pages, or 4KB PMTs
#define PDE_ADDR(p) \
(IS_LARGE(p) ? (((uint32_t)p) & PDE_FA) : (((uint32_t)p) & PDE_PTA))
// PTEs always point to 4KB pages
#define PTE_ADDR(p) (((uint32_t)(p)) & PTE_FA)
// everything has nine bits of permission flags
#define PERMS(p) (((uint32_t)(p)) & PERMS_MASK)
// extract the table indices from a 32-bit address
#define PDIX(v) ((((uint32_t)(v)) >> PDIX_SHIFT) & PIX2I_MASK)
#define PTIX(v) ((((uint32_t)(v)) >> PTIX_SHIFT) & PIX2I_MASK)
/*
** Types
*/
// page directory entries
// as a 32-bit word, in types.h
// typedef uint32_t pde_t;
// PDE for 4KB pages
typedef struct pdek_s {
uint_t p : 1; // present
uint_t rw : 1; // writable
uint_t us : 1; // user/supervisor
uint_t pwt : 1; // cache write-through
uint_t pcd : 1; // cache disable
uint_t a : 1; // accessed
uint_t avl1 : 1; // ignored (available)
uint_t ps : 1; // page size (must be 0)
uint_t avl2 : 4; // ignored (available)
uint_t fa : 20; // frame address
} pdek_f_t;
// PDE for 4MB pages
typedef struct pdem_s {
uint_t p : 1; // present
uint_t rw : 1; // writable
uint_t us : 1; // user/supervisor
uint_t pwt : 1; // cache write-through
uint_t pcd : 1; // cache disable
uint_t a : 1; // accessed
uint_t d : 1; // dirty
uint_t ps : 1; // page size (must be 1)
uint_t g : 1; // global
uint_t avl : 3; // ignored (available)
uint_t fa : 20; // frame address
} pdem_f_t;
// page table entries
// as a 32-bit word, in types.h
// typedef uint32_t pte_t;
// broken out into fields
typedef struct pte_s {
uint_t p : 1; // present
uint_t rw : 1; // writable
uint_t us : 1; // user/supervisor
uint_t pwt : 1; // cache write-through
uint_t pcd : 1; // cache disable
uint_t a : 1; // accessed
uint_t d : 1; // dirty
uint_t pat : 1; // page attribute table in use
uint_t g : 1; // global
uint_t avl : 3; // ignored (available)
uint_t fa : 20; // frame address
} ptef_t;
// page fault error code bits
// comment: meaning when 1 / meaning when 0
struct pfec_s {
uint_t p : 1; // page-level protection violation / !present
uint_t w : 1; // write / read
uint_t us : 1; // user-mode access / supervisor-mode access
uint_t rsvd : 1; // reserved bit violation / not
uint_t id : 1; // instruction fetch / data fetch
uint_t pk : 1; // protection-key violation / !pk
uint_t ss : 1; // shadow stack access / !ss
uint_t hlat : 1; // HLAT paging / ordinary paging or access rights
uint_t xtr1 : 7; // unused
uint_t sgz : 1; // SGX-specific access control violation / !SGX
uint_t xtr2 : 16; // more unused
};
typedef union pfec_u {
uint32_t u;
struct pfec_s s;
} pfec_t;
// Mapping descriptor for VA::PA mappings
typedef struct mapping_t {
uint32_t va_start; // starting virtual address for this range
uint32_t pa_start; // first physical address in the range
uint32_t pa_end; // last physical address in the range
uint32_t perm; // access control
} mapping_t;
/*
** Globals
*/
// created page directory for the kernel
extern pde_t *kpdir;
/*
** Prototypes
*/
/**
** Name: vm_init
**
** Initialize the VM module
**
** Note: should not be called until after the memory free list has
** been set up.
*/
void vm_init(void);
/**
** Name: vm_pagedup
**
** Duplicate a page of memory
**
** @param old Pointer to the first byte of a page
**
** @return a pointer to the new, duplicate page, or NULL
*/
void *vm_pagedup(void *old);
/**
** Name: vm_ptdup
**
** Duplicate a page directory entry
**
** @param dst Pointer to where the duplicate should go
** @param curr Pointer to the entry to be duplicated
**
** @return true on success, else false
*/
bool_t vm_ptdup(pde_t *dst, pde_t *curr);
/**
** Name: vm_getpte
**
** Return the address of the PTE corresponding to the virtual address
** 'va' within the address space controlled by 'pgdir'. If there is no
** page table for that VA and 'alloc' is true, create the necessary
** page table entries.
**
** @param pdir Pointer to the page directory to be searched
** @param va The virtual address we're looking for
** @param alloc Should we allocate a page table if there isn't one?
**
** @return A pointer to the page table entry for this VA, or NULL
*/
pte_t *vm_getpte(pde_t *pdir, const void *va, bool_t alloc);
/**
** Name: vm_mkkvm
**
** Create the kernel's page table hierarchy
*/
pde_t *vm_mkkvm(void);
/**
** Name: vm_mkuvm
**
** Create the page table hierarchy for a user process
*/
pde_t *vm_mkuvm(void);
/**
** Name: vm_set_kvm
**
** Switch the page table register to the kernel's page directory
*/
void vm_set_kvm(void);
/**
** Name: vm_set_uvm
**
** Switch the page table register to the page directory for a user process.
**
** @param p The PCB of the user process
*/
void vm_set_uvm(pcb_t *p);
/**
** Name: vm_add
**
** Add pages to the page hierarchy for a process, copying data into
** them if necessary.
**
** @param pdir Pointer to the page directory to modify
** @param wr "Writable" flag for the PTE
** @param sys "System" flag for the PTE
** @param va Starting VA of the range
** @param size Amount of physical memory to allocate
** @param data Pointer to data to copy, or NULL
** @param bytes Number of bytes to copy
**
** @return status of the allocation attempt
*/
int vm_add(pde_t *pdir, bool_t wr, bool_t sys, void *va, uint32_t size,
char *data, uint32_t bytes);
/**
** Name: vm_free
**
** Deallocate a page table hierarchy and all physical memory frames
** in the user portion.
**
** @param pdir Pointer to the page directory
*/
void vm_free(pde_t *pdir);
/*
** Name: vm_map
**
** Create PTEs for virtual addresses starting at 'va' that refer to
** physical addresses in the range [pa, pa+size-1]. We aren't guaranteed
** that va is page-aligned.
**
** @param pdir Page directory for this address space
** @param va The starting virtual address
** @param pa The starting physical address
** @param size Length of the range to be mapped
** @param perm Permission bits for the PTEs
*/
int vm_map(pde_t *pdir, void *va, uint32_t pa, uint32_t size, int perm);
/**
** Name: vm_uvmdup
**
** Create a duplicate of the user portio of an existing page table
** hierarchy. We assume that the "new" page directory exists and
** the system portions of it should not be touched.
**
** @param new New page directory
** @param old Existing page directory
**
** @return status of the duplication attempt
*/
int vm_uvmdup(pde_t *new, pde_t *old);
#endif /* !ASM_SRC */
#endif
|