]> git.corax.cc Git - corax/commitdiff
Implement functions for copying memory between page directories
authorMatthias Kruk <m@m10k.eu>
Sat, 5 Oct 2019 06:14:44 +0000 (15:14 +0900)
committerMatthias Kruk <m@m10k.eu>
Sat, 5 Oct 2019 06:14:44 +0000 (15:14 +0900)
kernel/arch/paging.c
kernel/arch/paging.h
kernel/core/process.c
kernel/include/arch.h
kernel/include/process.h

index bbe2c792fa05f001bc774fe0a2d3183148f27a62..176261a2bd792f277bef648338552e539d424ff9 100644 (file)
@@ -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;
index 248a01efb46c4d28838f5447d6fd62b01f07a3fe..805718248f6b5ce14285a776e5234dc777512585 100644 (file)
@@ -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 */
index f57276ea7f89dfe09737763bca5dad8085647f0e..2e994fad19e20806bb2d13d1949ef7d873fc57b6 100644 (file)
@@ -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);
+}
index 0cb7d74c1698f3f661a540efa2a76ecc925c7dee..b66894b36c6d932169fb7112d83ed95e6f76e765 100644 (file)
@@ -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 */
index 9c398f7f450fae8eb3ec2cca98cad56dcd772efb..f5ad6ba51b0efaa25391ad9283182b05e6a60fdc 100644 (file)
@@ -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 */