From: Matthias Kruk Date: Sat, 5 Oct 2019 06:14:44 +0000 (+0900) Subject: Implement functions for copying memory between page directories X-Git-Url: https://git.corax.cc/?a=commitdiff_plain;h=a5da67385f2f1a8041508ce62b7d35ef19f696b4;p=corax Implement functions for copying memory between page directories --- diff --git a/kernel/arch/paging.c b/kernel/arch/paging.c index bbe2c79..176261a 100644 --- a/kernel/arch/paging.c +++ b/kernel/arch/paging.c @@ -59,6 +59,8 @@ static const char *_str_pg_mode[] = { static int _pg_dir_add_region(pg_dir_t*, void*, u32_t, u32_t, u32_t); int _pg_dir_vpxlate(pg_dir_t*, u32_t, u32_t*); +int _pg_dir_pagesize(pg_dir_t*, u32_t, u32_t*); +int _pg_dir_xfer(pg_dir_t*, void*, pg_dir_t*, void*, u32_t); static void* _phys_alloc(u32_t size, u32_t align) { @@ -444,6 +446,169 @@ void* pg_dir_get_ustack(struct pagedir *pgdir) return(ret_val); } +int pg_dir_memcpy(struct pagedir *ddir, void *dvaddr, + struct pagedir *sdir, void *svaddr, u32_t bytes) +{ + int ret_val; + + ret_val = 0; + + /* a NULL pagedir means kernel */ + if(!ddir) { + ddir = &_kernel_pgdir; + } + + if(!sdir) { + sdir = &_kernel_pgdir; + } + + while(bytes > 0) { + int copied; + + copied = _pg_dir_xfer(ddir, dvaddr, sdir, svaddr, bytes); + + if(copied < 0) { + ret_val = copied; + break; + } + + dvaddr += copied; + svaddr += copied; + bytes -= copied; + ret_val += copied; + } + + return(ret_val); +} + +int _pg_dir_xfer(struct pagedir *ddir, void *dvaddr, + struct pagedir *sdir, void *svaddr, u32_t bytes) +{ + void *dpaddr; + void *spaddr; + int ret_val; + u32_t dpsize; + u32_t spsize; + u32_t srem; + u32_t drem; + + ret_val = -EFAULT; + + /* + * If one of the directories is the kernel pagedir, we can + * skip the lookup, which saves us some time. We can also ignore + * the page size in the case of the kernel pagedir, since all memory + * is identity mapped and present there. + */ + if(ddir == &_kernel_pgdir) { + dpaddr = dvaddr; + dpsize = 0x80000000; + ret_val = 0; + } else { + ret_val = _pg_dir_vpxlate(ddir, (u32_t)dvaddr, (u32_t*)&dpaddr); + + if(ret_val < 0) { + goto gtfo; + } + + ret_val = _pg_dir_pagesize(ddir, (u32_t)dvaddr, &dpsize); + } + + if(ret_val < 0) { + goto gtfo; + } + + if(sdir == &_kernel_pgdir) { + spaddr = svaddr; + spsize = 0x80000000; + ret_val = 0; + } else { + ret_val = _pg_dir_vpxlate(sdir, (u32_t)svaddr, (u32_t*)&spaddr); + + if(ret_val < 0) { + goto gtfo; + } + + ret_val = _pg_dir_pagesize(sdir, (u32_t)svaddr, &spsize); + } + + if(ret_val < 0) { + goto gtfo; + } + + /* + * Determine how much memory can be safely copied without crossing page + * boundaries, since memory that looks continguous to a process may actually + * not be, in physical memory. + * The amount of memory left from a given address to the end of the page + * can be computed by subtracting the offset into the page from the page size: + * + * offset = address & (PAGE_SIZE - 1) + * mem_size = PAGE_SIZE - offset + * + * This assumes that PAGE_SIZE is a power of 2, meaning (PAGE_SIZE - 1) gives + * us a bit pattern that is n zeroes followed by m ones. Or put simply, only + * one bit in PAGE_SIZE is set. + */ + + drem = dpsize - ((u32_t)dpaddr & (dpsize - 1)); + srem = spsize - ((u32_t)spaddr & (spsize - 1)); + + ret_val = drem < srem ? drem : srem; + memcpy(dpaddr, spaddr, ret_val); + +gtfo: + return(ret_val); +} + +int _pg_dir_pagesize(struct pagedir *pgdir, u32_t virt, u32_t *pagesize) +{ + int ret_val; + + ret_val = -EADDRNOTAVAIL; + + switch(_pg_flags & PG_MODE_MASK) { + case PG_MODE_LEGACY: { + struct page_table *dir; + u32_t i; + + dir = (struct page_table*)pgdir->pd_base; + i = virt >> 22; + + if(!(dir->pt_entries[i] & PAGE_ATTR_PRESENT)) { + break; + } + + /* if PAGE_ATTR_SIZE is set in the page directory, we have a 4MB page */ + if(dir->pt_entries[i] & PAGE_ATTR_SIZE) { + *pagesize = PAGE_SIZE_LARGE; + ret_val = 0; + break; + } + + dir = (struct page_table*)(dir->pt_entries[i] & 0xfffff000); + i = (virt >> 12) & 0x3ff; + + if(!(dir->pt_entries[i] & PAGE_ATTR_PRESENT)) { + break; + } + + /* normal, present page -> 4KB page */ + *pagesize = PAGE_SIZE; + ret_val = 0; + + break; + } + + case PG_MODE_PAE: + default: + ret_val = -ENOSYS; + break; + } + + return(ret_val); +} + int _pg_dir_vpxlate(struct pagedir *pgdir, u32_t virt, u32_t *phys) { int ret_val; diff --git a/kernel/arch/paging.h b/kernel/arch/paging.h index 248a01e..8057182 100644 --- a/kernel/arch/paging.h +++ b/kernel/arch/paging.h @@ -93,5 +93,6 @@ struct pae_page_table { void* pg_frame_alloc_start(void); void* pg_frame_alloc_end(void); void pg_frame_free(void*); +int pg_dir_memcpy(struct pagedir*, void*, struct pagedir*, void*, u32_t); #endif /* __PAGING_H */ diff --git a/kernel/core/process.c b/kernel/core/process.c index f57276e..2e994fa 100644 --- a/kernel/core/process.c +++ b/kernel/core/process.c @@ -167,3 +167,21 @@ process_t* process_get_current(void) return(ret_val); } + +int process_memcpy_ptok(process_t *proc, void *dst, void *src, u32_t bytes) +{ + int ret_val; + + ret_val = pg_dir_memcpy(NULL, dst, proc->p_pgdir, src, bytes); + + return(ret_val); +} + +int process_memcpy_ktop(process_t *proc, void *dst, void *src, u32_t bytes) +{ + int ret_val; + + ret_val = pg_dir_memcpy(proc->p_pgdir, dst, NULL, src, bytes); + + return(ret_val); +} diff --git a/kernel/include/arch.h b/kernel/include/arch.h index 0cb7d74..b66894b 100644 --- a/kernel/include/arch.h +++ b/kernel/include/arch.h @@ -91,4 +91,6 @@ void* pg_dir_get_pdbr(pg_dir_t*); void* pg_dir_get_kstack(pg_dir_t*); void* pg_dir_get_ustack(pg_dir_t*); +int pg_dir_memcpy(pg_dir_t*, void*, pg_dir_t*, void*, u32_t); + #endif /* __ARCH_H */ diff --git a/kernel/include/process.h b/kernel/include/process.h index 9c398f7..f5ad6ba 100644 --- a/kernel/include/process.h +++ b/kernel/include/process.h @@ -32,4 +32,7 @@ int process_flookup(process_t*, int); process_t* process_get_current(void); +int process_memcpy_ptok(process_t*, void*, void*, u32_t); +int process_memcpy_ktop(process_t*, void*, void*, u32_t); + #endif /* __PROCESS_H */