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)
{
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;