mirror of
https://github.com/kenshineto/kern.git
synced 2025-04-10 12:37:26 +00:00
444 lines
12 KiB
C
444 lines
12 KiB
C
/**
|
|
** @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
|