/*
 * memmap.c	- show kernel memory map
 *
 * Copyright (c) 1992 Branko Lankester
 *
 */

#define __KERNEL__
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include "ps.h"
#include "psdata.h"

int pg_shift = 2;
int Sum = 0;
unsigned short *memmap;
unsigned pages;
unsigned long low_memory;

void show_m(struct task_struct *task);
void prstate(int addr, int state);

int
main(int argc, char *argv[])
{
    unsigned long _task;
    struct task_struct *taskp, task;
    int nr, pid = 0;

    if (open_psdb()) {
	perror("cannot open psdatabase");
	exit(2);
    }

    _task = k_addr("_task");

    if (argc == 2) {
	pid = atoi(argv[1]);
    }
    for (nr = 1; nr < NR_TASKS; ++nr) {
	kmemread(&taskp, _task + 4*nr, 4);
	if (taskp) {
	    kmemread(&task, (unsigned long) taskp, sizeof(task));
	    if (pid && pid != task.pid)
		continue;
	    if (task.state != TASK_ZOMBIE)
		show_m(&task);
	}
    }
    exit(0);
}

unsigned data_pag, bss_pag, brk_pag;

#define	SHARED		0x001000
#define	SWAPPED		0x002000
#define	MMAP		0x004000


void
show_m(struct task_struct *task)
{
    int i;
    unsigned long buf[PAGE_SIZE/4], *pte;
    unsigned long pdir, ptbl;
    unsigned addr;
    unsigned state, old_state= ~1, map_nr;
    int size=0, resident=0;
    int wr=0, dirty=0, acc=0, share=0, trs=0, lrs=0;
    int sh_lib=0, dt=0;
    int tpag = task->mm->end_code / PAGE_SIZE;
    int ndup = 0;

    if (memmap == NULL)
	get_memmap();

    data_pag = task->mm->end_code/PAGE_SIZE;
    bss_pag = task->mm->end_data/PAGE_SIZE;
    brk_pag = (task->mm->brk+0xfff) / PAGE_SIZE;

    printf("\nPid %d: %-8.8s\n", task->pid, task->comm);
    pdir = task->tss.cr3 + (task->mm->start_code >> 20);
    addr = 0;
    for (i = 0; i < 0x400; ++i, pdir += 4) {
	ptbl = get_kword(pdir);
	if (ptbl == 0) {
	    tpag -= 1024;
	    addr += 1024;
	    continue;
	}
	if (i >= 0x300)
	    continue;
	kmemread(buf, ptbl & 0xfffff000, sizeof buf);

	for (pte = buf; pte < &buf[1024]; ++pte, ++addr, --tpag) {
	    state = 0;

	    if (*pte != 0) {
		++size;
		if (*pte & 1) {
		    state |= (*pte & 0xfff);

		    ++resident;
		    if (tpag > 0) {
			++trs;
			/*
			if (*pte & 0x40) ++dt;
			*/
		    }
		    if (i == 15) {
			++lrs;
			if (*pte & 0x40) ++dt;
		    }
		    if ((map_nr = MAP_NR(*pte)) < pages) {
			state |= memmap[map_nr] << 16;
			if ((memmap[map_nr]&0x7fff) > 1) {
			    state |= SHARED;
			    if (i == 15)
				++sh_lib;
			    else
				++share;
			}
		    } else
			state |= MMAP;

		    if (*pte & 2) ++wr;
		    if (*pte & 0x40) ++dirty;
		    if (*pte & 0x20) ++acc;

		} else
		    if (*pte)
			state = SWAPPED|(*pte & 0xfff);

	    }
	    if (state != old_state) {
		switch (ndup) {
		    case 2: prstate(addr-2, old_state);
		    case 1: prstate(addr-1, old_state);
		    case 0: break;
		    default:
			    printf("      ** %d\n", ndup - 1);
			    prstate(addr-1, old_state);
		}
		prstate(addr, state);
		ndup = 0;
		old_state = state;
	    } else
		++ndup;

	}
    }
    printf("accessed: %d,  dirty: %d,  writable: %d\n", acc, dirty, wr);
}

char *types[] = {
    "TEXT", "DATA", "BSS", "---", "LIB", "STACK",
};

void
prstate(int addr, int state)
{
    int type;

    printf("%8lx: ", addr * PAGE_SIZE);

    type = 0;
    if (addr >= data_pag)
	++type;
    if (addr >= bss_pag)
	++type;
    if (addr >= brk_pag)
	++type;
    if (addr >= 0x3c00)
	++type;
    if (addr >= 0xbf000)
	++type;
    printf("%-5s  ", types[type]);

    if (state & 0xffff) {

	if (state & SWAPPED)
	    printf("<swapped> (%#x)\n", state&0xfff);
	else
	    printf("%c%c%c%c%c%c   %3d\n",
		state & 0x20 ? 'A' : '-',
		state & 0x40 ? 'D' : '-',
		state & 0x02 ? 'W' : '-',
		state & 0x200 ? 'C' : '-',
		state & MMAP ? 'M' : ' ',
		state & 0x80000000 ? 'R' : ' ',
		(state >> 16) & 0x7fff);
    } else
	printf("\n");
}

void
get_memmap(void)
{
    static unsigned long _mem_map;

    pages = get_kword(k_addr("_high_memory")) / 4096;
    if (memmap == NULL) {
	_mem_map = get_kword(k_addr("_mem_map"));
	memmap = (unsigned short *) xmalloc(pages * sizeof *memmap);
    }
    kmemread(memmap, _mem_map, pages * sizeof *memmap);
}
