/*
 * ps_mem.c -	mmap() kernel memory
 *
 * Copyright (c) 1993 Branko Lankester
 * 
 */

#define __KERNEL__
#include <string.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <linux/mm.h>
#include <stdio.h>
#include "ps.h"
#include "psdata.h"


extern int kmemfd;

caddr_t kbase;

char *mmapped;
static unsigned long memory_start;
static unsigned long high_memory;
static unsigned short *memmap;

int
mmap_page(unsigned long addr)
{
    caddr_t r;
    unsigned mapnr;

    if (addr >= high_memory) {
	fprintf(stderr, "trying to mmap %lx\n", addr);
	return -1;
    }
    mapnr = MAP_NR(addr);
    if (mmapped[mapnr])
	return 0;
    if (memmap[mapnr] == 0) {
	fprintf(stderr, "page not in use; %lx\n", addr);
	return -1;
    }
    addr &= ~0xfff;
    r = mmap(kbase+addr, 4096, PROT_READ, MAP_SHARED|MAP_FIXED, kmemfd, addr);
    if ((int) r == -1) {
	perror("mmap_page: mmap");
	return -1;
    }
    if (memmap[mapnr] == 1) {
	munmap(kbase+addr, 4096);
	fprintf(stderr, "page not used; %lx\n", addr);
	return -1;
    }
    mmapped[mapnr] = 1;
    return 0;
}

int
mmap_init(void)
{
    if (high_memory)	/* initialize only once */
	return 0;
    kmemread(&high_memory, k_addr("_high_memory"), sizeof(high_memory));
    kmemread(&memory_start, k_addr("_memory_start"), sizeof(memory_start));
    high_memory = (high_memory + 0xfff) & ~0xfff;
    memory_start = (memory_start + 0xfff) & ~0xfff;
    memory_start += high_memory / 4096 * sizeof(*memmap);
    memory_start += 0xfff;
    memory_start &= ~0xfff;
    kbase = mmap((caddr_t) 0, memory_start, PROT_READ, MAP_SHARED, kmemfd, 0L);
    if (kbase == (caddr_t) -1) {
	perror("mmap_init: mmap");
	return -1;
    }
    memmap = KPTR(KWORD(k_addr("_mem_map")));
    mmapped = xmalloc(high_memory/4096);
    memset(mmapped, 0, high_memory/4096);
    memset(mmapped, 1, memory_start/4096);
    return 0;
}

/*
 * unmap unused pages
 */
void
mmap_cleanup(void)
{
    int i, pages = high_memory/4096;

    for (i = memory_start/4096; i < pages; ++i) {
	if (mmapped[i] && memmap[i] == 1) {
	    munmap(kbase + i*4096, 4096);
	    mmapped[i] = 0;
	}
    }
}

void
munmap_mem(void)
{
    if (munmap(kbase+memory_start, high_memory-memory_start) == -1)
	perror("munmap");
    memset(mmapped, 0, high_memory/4096);
    memset(mmapped, 1, memory_start/4096);
}
