Goto sanos source index

//
// pdir.c
//
// Page directory 
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 
// 1. Redistributions of source code must retain the above copyright 
//    notice, this list of conditions and the following disclaimer.  
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.  
// 3. Neither the name of the project nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission. 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
// SUCH DAMAGE.
// 

#include <os/krnl.h>

pte_t *pdir = (pte_t *) PAGEDIR_ADDRESS; // Page directory
pte_t *ptab = (pte_t *) PTBASE;          // Page tables

void map_page(void *vaddr, unsigned long pfn, unsigned long flags) {
  // Allocate page table if not already done
  if ((GET_PDE(vaddr) & PT_PRESENT) == 0) {
    unsigned long pdfn;

    pdfn = alloc_pageframe('PTAB');
    if (USERSPACE(vaddr)) {
      SET_PDE(vaddr, PTOB(pdfn) | PT_PRESENT | PT_WRITABLE | PT_USER);
    } else {
      SET_PDE(vaddr, PTOB(pdfn) | PT_PRESENT | PT_WRITABLE);
    }

    memset(ptab + PDEIDX(vaddr) * PTES_PER_PAGE, 0, PAGESIZE);
    register_page_table(pdfn);
  }

  // Map page frame into address space
  SET_PTE(vaddr, PTOB(pfn) | flags);
}

void unmap_page(void *vaddr) {
  SET_PTE(vaddr, 0);
  invlpage(vaddr);
}

unsigned long virt2phys(void *vaddr) {
  return ((GET_PTE(vaddr) & PT_PFNMASK) + PGOFF(vaddr));
}

unsigned long virt2pfn(void *vaddr) {
  return BTOP(GET_PTE(vaddr) & PT_PFNMASK);
}

pte_t get_page_flags(void *vaddr) {
  return GET_PTE(vaddr) & PT_FLAGMASK;
}

void set_page_flags(void *vaddr, unsigned long flags) {
  SET_PTE(vaddr, (GET_PTE(vaddr) & PT_PFNMASK) | flags);
  invlpage(vaddr);
}

int page_mapped(void *vaddr) {
  if ((GET_PDE(vaddr) & PT_PRESENT) == 0) return 0;
  if ((GET_PTE(vaddr) & PT_PRESENT) == 0) return 0;
  return 1;
}

int page_directory_mapped(void *vaddr) {
  return (GET_PDE(vaddr) & PT_PRESENT) != 0;
}

void unguard_page(void *vaddr) {
  SET_PTE(vaddr, (GET_PTE(vaddr) & ~PT_GUARD) | PT_USER);
  invlpage(vaddr);
}

void clear_dirty(void *vaddr) {
  SET_PTE(vaddr, GET_PTE(vaddr) & ~PT_DIRTY);
  invlpage(vaddr);
}

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 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;
    }
  }
}

void init_pdir() {
  unsigned long i;

  // Clear identity mapping of the first 4 MB made by the os loader
  for (i = 0; i < PTES_PER_PAGE; i++) SET_PTE(PTOB(i), 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;
}

static print_virtmem(struct proc_file *pf, char *start, char *end, unsigned long tag) {
  char tagname[5];
  tag2str(tag, tagname);

  pprintf(pf, "%08x %08x %8dK %-4s\n", start, end - 1, (end - start) / 1024, tagname);
}

int virtmem_proc(struct proc_file *pf, void *arg) {
  char *vaddr;
  char *start;
  unsigned long curtag;
  int total = 0;

  pprintf(pf, "start    end           size type\n");
  pprintf(pf, "-------- -------- --------- ----\n");

  start = vaddr = NULL;
  curtag = 0;
  while (1) {
    if ((GET_PDE(vaddr) & PT_PRESENT) == 0) {
      if (start != NULL) {
        print_virtmem(pf, start, vaddr, curtag);
        start = NULL;
      }

      vaddr += PTES_PER_PAGE * PAGESIZE;
    } else {
      pte_t pte = GET_PTE(vaddr);
      unsigned long tag = pfdb[pte >> PT_PFNSHIFT].tag;

      if (pte & PT_PRESENT) {
        if (start == NULL)  {
          start = vaddr;
          curtag = tag;
        } else if (tag != curtag) {
          print_virtmem(pf, start, vaddr, curtag);
          start = vaddr;
          curtag = tag;
        }

        total += PAGESIZE;
      } else {
        if (start != NULL) {
          print_virtmem(pf, start, vaddr, curtag);
          start = NULL;
        }
      }

      vaddr += PAGESIZE;
    }

    if (!vaddr) break;
  }

  if (start) print_virtmem(pf, start, vaddr, curtag);
  pprintf(pf, "total             %8dK\n", total / 1024);

  return 0;
}

int pdir_stat(void *addr, int len, struct pdirstat *buf) {
  char *vaddr;
  char *end;
  pte_t pte;

  memset(buf, 0, sizeof(struct pdirstat));
  vaddr = (char *) addr;
  end = vaddr + len;
  while (vaddr < end) {
    if ((GET_PDE(vaddr) & PT_PRESENT) == 0) {
      vaddr += PTES_PER_PAGE * PAGESIZE;
      vaddr = (char *) ((unsigned long) vaddr & ~(PTES_PER_PAGE * PAGESIZE - 1));
    } else {
      pte = GET_PTE(vaddr);
      if (pte & PT_PRESENT) {
        buf->present++;
        
        if (pte & PT_WRITABLE) {
          buf->readwrite++;
        } else {
          buf->readonly++;
        }

        if (pte & PT_USER) {
          buf->user++;
        } else {
          buf->kernel++;
        }

        if (pte & PT_ACCESSED) buf->accessed++;
        if (pte & PT_DIRTY) buf->dirty++;
      }

      vaddr += PAGESIZE;
    }
  }

  return 0;
}