Goto sanos source index

//
// pframe.c
//
// Page frame database routines
//
// 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>

#define MAX_MEMTAGS           128

unsigned long freemem;        // Number of pages free memory
unsigned long totalmem;       // Total number of pages of memory (bad pages excluded)
unsigned long maxmem;         // First unavailable memory page
struct pageframe *pfdb;       // Page frame database      
struct pageframe *freelist;   // List of free pages

void panic(char *msg);

unsigned long alloc_pageframe(unsigned long tag) {
  struct pageframe *pf;

  if (freemem == 0) panic("out of memory");

  pf = freelist;
  freelist = pf->next;
  freemem--;

  pf->tag = tag;
  pf->next = NULL;

  return pf - pfdb;
}

unsigned long alloc_linear_pageframes(int pages, unsigned long tag) {
  struct pageframe *pf;
  struct pageframe *prevpf;

  if (pages == 1) return alloc_pageframe(tag);

  if ((int) freemem < pages) return 0xFFFFFFFF;

  prevpf = NULL;
  pf = freelist;
  while (pf) {
    if (pf - pfdb + pages < (int) maxmem) {
      int n;

      for (n = 0; n < pages; n++) {
        if (pf[n].tag != 'FREE') break;
        if (n != 0 && pf[n - 1].next != &pf[n]) break;
      }

      if (n == pages) {
        if (prevpf) {
          prevpf->next = pf[pages - 1].next;
        } else {
          freelist = pf[pages - 1].next;
        }

        for (n = 0; n < pages; n++) {
          pf[n].tag = tag;
          pf[n].next = NULL;
        }

        freemem -= pages;

        return pf - pfdb;
      }
    }

    prevpf = pf;
    pf = pf->next;
  }

  return 0xFFFFFFFF;
}

void free_pageframe(unsigned long pfn) {
  struct pageframe *pf;

  pf = pfdb + pfn;
  pf->tag = 'FREE';
  pf->next = freelist;
  freelist = pf;
  freemem++;
}

void set_pageframe_tag(void *addr, unsigned int len, unsigned long tag) {
  char *vaddr = (char *) addr;
  char *vend = vaddr + len;

  while (vaddr < vend) {
    unsigned long pfn = virt2phys(vaddr) >> PAGESHIFT;
    pfdb[pfn].tag = tag;
    vaddr += PAGESIZE;
  }
}

int memmap_proc(struct proc_file *pf, void *arg) {
  struct memmap *mm = &syspage->bootparams.memmap;
  int i;

  for (i = 0; i < mm->count; i++) {
    char *type;

    switch (mm->entry[i].type) {
      case MEMTYPE_RAM: type = "RAM "; break;
      case MEMTYPE_RESERVED: type = "RESV"; break;
      case MEMTYPE_ACPI: type = "ACPI"; break;
      case MEMTYPE_NVS: type = "NVS "; break;
      default: type = "MEM?"; break;
    }

    pprintf(pf, "0x%08x-0x%08x type %s %8d KB\n", 
      (unsigned long) mm->entry[i].addr, 
      (unsigned long) (mm->entry[i].addr + mm->entry[i].size) - 1,
      type,
      (unsigned long) mm->entry[i].size / 1024); 
  }

  return 0;
}

void tag2str(unsigned long tag, char *str) {
  if (tag & 0xFF000000) *str++ = (char) ((tag >> 24) & 0xFF);
  if (tag & 0x00FF0000) *str++ = (char) ((tag >> 16) & 0xFF);
  if (tag & 0x0000FF00) *str++ = (char) ((tag >> 8) & 0xFF);
  if (tag & 0x000000FF) *str++ = (char) (tag & 0xFF);
  *str++ = 0;
}

int memusage_proc(struct proc_file *pf, void *arg) {
  unsigned int num_memtypes = 0;
  struct { unsigned long tag; int pages; } memtype[MAX_MEMTAGS];
  unsigned long tag;
  unsigned int n;
  unsigned int m;

  for (n = 0; n < maxmem; n++) {
    tag = pfdb[n].tag;

    m = 0;
    while (m < num_memtypes && tag != memtype[m].tag) m++;

    if (m < num_memtypes) {
      memtype[m].pages++;
    } else if (m < MAX_MEMTAGS) {
      memtype[m].tag = tag;
      memtype[m].pages = 1;
      num_memtypes++;
    }
  }

  for (n = 0; n < num_memtypes; n++) {
    char tagname[5];
    tag2str(memtype[n].tag, tagname);
    pprintf(pf, "%-4s    %8d KB\n", tagname, memtype[n].pages * (PAGESIZE / 1024));
  }

  return 0;
}

int memstat_proc(struct proc_file *pf, void *arg) {
  pprintf(pf, "Memory %dMB total, %dKB used, %dKB free, %dKB reserved\n", 
          maxmem * PAGESIZE / (1024 * 1024), 
          (totalmem - freemem) * PAGESIZE / 1024, 
          freemem * PAGESIZE / 1024, (maxmem - totalmem) * PAGESIZE / 1024);
  
  return 0;
}

int physmem_proc(struct proc_file *pf, void *arg) {
  unsigned int n;

  for (n = 0; n < maxmem; n++) {
    if (n % 64 == 0) {
      if (n > 0) pprintf(pf, "\n");
      pprintf(pf, "%08X ", PTOB(n));
    }

    if (pfdb[n].tag == 'FREE') {
      pprintf(pf, ".");
    } else if (pfdb[n].tag == 'RESV') {
      pprintf(pf, "-");
    } else if (pfdb[n].tag == 0) {
      pprintf(pf, "?");
    } else {
      char tagname[5];
      tag2str(pfdb[n].tag, tagname);
      pprintf(pf, "%c", *tagname);
    }
  }

  pprintf(pf, "\n");
  return 0;
}

void init_pfdb() {
  unsigned long heap;
  unsigned long pfdbpages;
  unsigned long i, j;
  unsigned long memend;
  pte_t *pt;
  struct pageframe *pf;
  struct memmap *memmap;

  // Register page directory
  register_page_dir(virt2pfn(pdir));

  // Calculates number of pages needed for page frame database
  memend = syspage->ldrparams.memend;
  heap = syspage->ldrparams.heapend;
  pfdbpages = PAGES((memend / PAGESIZE) * sizeof(struct pageframe));
  if ((pfdbpages + 2) * PAGESIZE + heap >= memend) panic("not enough memory for page table database");

  // Intialize page tables for mapping the page frame database into kernel space
  set_page_dir_entry(&pdir[PDEIDX(PFDBBASE)], heap | PT_PRESENT | PT_WRITABLE);
  set_page_dir_entry(&pdir[PDEIDX(PFDBBASE) + 1], (heap + PAGESIZE) | PT_PRESENT | PT_WRITABLE);
  pt = (pte_t *) heap;
  heap += 2 * PAGESIZE;
  memset(pt, 0, 2 * PAGESIZE);
  register_page_table(BTOP(pt));
  register_page_table(BTOP(pt) + 1);

  // Allocate and map pages for page frame database
  for (i = 0; i < pfdbpages; i++) {
    set_page_table_entry(&pt[i], heap | PT_PRESENT | PT_WRITABLE);
    heap += PAGESIZE;
  }

  // Initialize page frame database
  maxmem = syspage->ldrparams.memend / PAGESIZE;
  totalmem = 0;
  freemem = 0;

  pfdb = (struct pageframe *) PFDBBASE;
  memset(pfdb, 0, pfdbpages * PAGESIZE);
  for (i = 0; i < maxmem; i++) pfdb[i].tag = 'BAD';

  // Add all memory from memory map to page frame database
  memmap = &syspage->bootparams.memmap;
  for (i = 0; i < (unsigned long) memmap->count; i++) {
    unsigned long first = (unsigned long) memmap->entry[i].addr / PAGESIZE;
    unsigned long last = first + (unsigned long) memmap->entry[i].size / PAGESIZE;

    if (first >= maxmem) continue;
    if (last >= maxmem) last = maxmem;

    if (memmap->entry[i].type == MEMTYPE_RAM) {
      for (j = first; j < last; j++) pfdb[j].tag = 'FREE';
      totalmem += (last - first);
    } else if (memmap->entry[i].type == MEMTYPE_RESERVED) {
      for (j = first; j < last; j++) pfdb[j].tag = 'RESV';
    }
  }

  // Reserve physical page 0 for BIOS
  pfdb[0].tag = 'RESV';
  totalmem--;

  // Add interval [heapstart:heap] to pfdb as page table pages
  for (i = syspage->ldrparams.heapstart / PAGESIZE; i < heap / PAGESIZE; i++) pfdb[i].tag = 'PTAB';

  // Reserve DMA buffers at 0x10000 (used by floppy driver)
  for (i = DMA_BUFFER_START / PAGESIZE; i < DMA_BUFFER_START / PAGESIZE + DMA_BUFFER_PAGES; i++) pfdb[i].tag = 'DMA';

  // Fixup tags for pfdb and syspage and intial tcb
  set_pageframe_tag(pfdb, pfdbpages * PAGESIZE, 'PFDB');
  set_pageframe_tag(syspage, PAGESIZE, 'SYS');
  set_pageframe_tag(self(), TCBSIZE, 'TCB');
  set_pageframe_tag((void *) INITRD_ADDRESS, syspage->ldrparams.initrd_size, 'BOOT');

  // Insert all free pages into free list
  pf = pfdb + maxmem;
  do {
    pf--;

    if (pf->tag == 'FREE') {
      pf->next = freelist;
      freelist = pf;
      freemem++;
    }
  } while (pf > pfdb);
}