int mem_access(void *vaddr, int size, pte_t access) { unsigned long addr; unsigned long next; pte_t pte; addr = (unsigned long) vaddr; next = (addr & ~PAGESIZE) + PAGESIZE; while (1) { if ((GET_PDE(addr) & PT_PRESENT) == 0) return 0; pte = GET_PTE(addr); if ((pte & access) != access) { if (pte & PT_FILE) { if (fetch_page((void *) PAGEADDR(addr)) < 0) return 0; if ((GET_PTE(addr) & access) != access) return 0; } else { return 0; } } size -= next - addr; if (size <= 0) break; addr = next; next += PAGESIZE; } return 1; }
int vmfree(void *addr, unsigned long size, int type) { struct filemap *fm = NULL; int pages = PAGES(size); int i, rc; char *vaddr; if (size == 0) return 0; addr = (void *) PAGEADDR(addr); if (!valid_range(addr, size)) return -EINVAL; if (type & (MEM_DECOMMIT | MEM_RELEASE)) { vaddr = (char *) addr; for (i = 0; i < pages; i++) { if (page_directory_mapped(vaddr)) { pte_t flags = get_page_flags(vaddr); unsigned long pfn = BTOP(virt2phys(vaddr)); if (flags & PT_FILE) { handle_t h = (flags & PT_PRESENT) ? pfdb[pfn].owner : pfn; struct filemap *newfm = (struct filemap *) hlookup(h); if (newfm != fm) { if (fm) { if (fm->pages == 0) { rc = free_filemap(fm); } else { rc = unlock_filemap(fm); } if (rc < 0) return rc; } fm = newfm; rc = wait_for_object(fm, INFINITE); if (rc < 0) return rc; } fm->pages--; unmap_page(vaddr); if (flags & PT_PRESENT) free_pageframe(pfn); } else if (flags & PT_PRESENT) { unmap_page(vaddr); free_pageframe(pfn); } } vaddr += PAGESIZE; } } if (fm) { if (fm->pages == 0) { rc = free_filemap(fm); } else { rc = unlock_filemap(fm); } if (rc < 0) return rc; } else if (type & MEM_RELEASE) { rmap_free(vmap, BTOP(addr), pages); } return 0; }
void *vmmap(void *addr, unsigned long size, int protect, struct file *filp, off64_t offset, int *rc) { int pages = PAGES(size); unsigned long flags = pte_flags_from_protect(protect); struct filemap *fm; int i; char *vaddr; if (rc) *rc = 0; if (size == 0 || flags == 0xFFFFFFFF) { if (rc) *rc = -EINVAL; return NULL; } addr = (void *) PAGEADDR(addr); if (addr == NULL) { addr = (void *) PTOB(rmap_alloc(vmap, pages)); if (addr == NULL) { if (rc) *rc = -ENOMEM; return NULL; } } else { if (rmap_reserve(vmap, BTOP(addr), pages)) { if (rc) *rc = -ENOMEM; return NULL; } } fm = (struct filemap *) kmalloc(sizeof(struct filemap)); if (!fm) { rmap_free(vmap, BTOP(addr), pages); if (rc) *rc = -ENOMEM; return NULL; } init_object(&fm->object, OBJECT_FILEMAP); fm->self = halloc(&fm->object); fm->file = halloc(&filp->iob.object); if (fm->self < 0 || fm->file < 0) { if (rc) *rc = -ENFILE; return NULL; } hprotect(fm->self); hprotect(fm->file); fm->offset = offset; fm->pages = pages; fm->object.signaled = 1; fm->addr = addr; fm->size = size; fm->protect = flags | PT_FILE; vaddr = (char *) addr; flags = (flags & ~PT_USER) | PT_FILE; for (i = 0; i < pages; i++) { map_page(vaddr, fm->self, flags); vaddr += PAGESIZE; } return addr; }
int vmsync(void *addr, unsigned long size) { struct filemap *fm = NULL; int pages = PAGES(size); int i, rc; char *vaddr; if (size == 0) return 0; addr = (void *) PAGEADDR(addr); if (!valid_range(addr, size)) return -EINVAL; vaddr = (char *) addr; for (i = 0; i < pages; i++) { if (page_directory_mapped(vaddr)) { pte_t flags = get_page_flags(vaddr); if ((flags & (PT_FILE | PT_PRESENT | PT_DIRTY)) == (PT_FILE | PT_PRESENT | PT_DIRTY)) { unsigned long pfn = BTOP(virt2phys(vaddr)); struct filemap *newfm = (struct filemap *) hlookup(pfdb[pfn].owner); if (newfm != fm) { if (fm) { rc = unlock_filemap(fm); if (rc < 0) return rc; } fm = newfm; rc = wait_for_object(fm, INFINITE); if (rc < 0) return rc; } rc = save_file_page(fm, vaddr); if (rc < 0) return rc; } } vaddr += PAGESIZE; } if (fm) { rc = unlock_filemap(fm); if (rc < 0) return rc; } return 0; }
int str_access(char *s, pte_t access) { pte_t pte; while (1) { if ((GET_PDE(s) & PT_PRESENT) == 0) return 0; pte = GET_PTE(s); if ((pte & access) != access) { if (pte & PT_FILE) { if (fetch_page((void *) PAGEADDR(s)) < 0) return 0; if ((GET_PTE(s) & access) != access) return 0; } else { return 0; } } while (1) { if (!*s) return 1; s++; if (PGOFF(s) == 0) break; } } }
int vmprotect(void *addr, unsigned long size, int protect) { int pages = PAGES(size); int i; char *vaddr; unsigned long flags; if (size == 0) return 0; addr = (void *) PAGEADDR(addr); if (!valid_range(addr, size)) return -EINVAL; flags = pte_flags_from_protect(protect); if (flags == 0xFFFFFFFF) return -EINVAL; vaddr = (char *) addr; for (i = 0; i < pages; i++) { if (page_mapped(vaddr)) { set_page_flags(vaddr, (get_page_flags(vaddr) & ~PT_PROTECTMASK) | flags); } vaddr += PAGESIZE; } return 0; }
int pdir_proc(struct proc_file *pf, void *arg) { char *vaddr; pte_t pte; int lines = 0; int ma = 0; int us = 0; int su = 0; int ro = 0; int rw = 0; int ac = 0; int dt = 0; int gd = 0; int fi = 0; pprintf(pf, "virtaddr physaddr flags\n"); pprintf(pf, "-------- -------- ------\n"); vaddr = NULL; while (1) { if ((GET_PDE(vaddr) & PT_PRESENT) == 0) { vaddr += PTES_PER_PAGE * PAGESIZE; } else { pte = GET_PTE(vaddr); if (pte & PT_PRESENT) { ma++; if (pte & PT_WRITABLE) { rw++; } else { ro++; } if (pte & PT_USER) { us++; } else { su++; } if (pte & PT_ACCESSED) ac++; if (pte & PT_DIRTY) dt++; if (pte & PT_GUARD) gd++; if (pte & PT_FILE) fi++; pprintf(pf, "%08x %08x %c%c%c%c%c%c\n", vaddr, PAGEADDR(pte), (pte & PT_WRITABLE) ? 'w' : 'r', (pte & PT_USER) ? 'u' : 's', (pte & PT_ACCESSED) ? 'a' : ' ', (pte & PT_DIRTY) ? 'd' : ' ', (pte & PT_GUARD) ? 'g' : ' ', (pte & PT_FILE) ? 'f' : ' '); } vaddr += PAGESIZE; } if (!vaddr) break; } pprintf(pf, "\ntotal:%d usr:%d sys:%d rw: %d ro: %d acc: %d dirty: %d guard:%d file:%d\n", ma, us, su, rw, ro, ac, dt, gd, fi); return 0; }
void *vmalloc(void *addr, unsigned long size, int type, int protect, unsigned long tag, int *rc) { int pages = PAGES(size); unsigned long flags = pte_flags_from_protect(protect); int i; if (rc) *rc = 0; if (size == 0) { if (rc) *rc = -EINVAL; return NULL; } if ((type & MEM_COMMIT) != 0 && flags == 0xFFFFFFFF) { if (rc) *rc = -EINVAL; return NULL; } addr = (void *) PAGEADDR(addr); if (!addr && (type & MEM_COMMIT) != 0) type |= MEM_RESERVE; if (!tag) tag = 'VM'; if (type & MEM_RESERVE) { if (addr == NULL) { if (type & MEM_ALIGN64K) { addr = (void *) PTOB(rmap_alloc_align(vmap, pages, 64 * 1024 / PAGESIZE)); } else { addr = (void *) PTOB(rmap_alloc(vmap, pages)); } if (addr == NULL) { if (rc) *rc = -ENOMEM; return NULL; } } else { if (rmap_reserve(vmap, BTOP(addr), pages)) { if (rc) *rc = -ENOMEM; return NULL; } } } else { if (!valid_range(addr, size)) { if (rc) *rc = -EFAULT; return NULL; } } if (type & MEM_COMMIT) { char *vaddr; unsigned long pfn; vaddr = (char *) addr; for (i = 0; i < pages; i++) { if (page_mapped(vaddr)) { set_page_flags(vaddr, flags | PT_PRESENT); } else { pfn = alloc_pageframe(tag); if (pfn == 0xFFFFFFFF) { if (rc) *rc = -ENOMEM; return NULL; } map_page(vaddr, pfn, flags | PT_PRESENT); memset(vaddr, 0, PAGESIZE); } vaddr += PAGESIZE; } } return addr; }