/*
 * Copyright (C) 2000-2003 ASANO Masahiro
 */

#include <unistd.h>
#include "crash.h"

PRIVATE addr_t page(), vm_area_struct(), zone(), pglist_data();
const commandtable_t command_page =
	{"page", page, "[-a] [address] | -n PFN", "print page (or mem_map_t) table\n  -n  specify page frame number"};
const commandtable_t command_vm_area_struct =
	{"vm_area_struct", vm_area_struct, "address", "print vm_area_struct table"};
const commandtable_t command_zone =
	{"zone", zone, "[-f] [address]", "print zone table"};
const commandtable_t command_pglist_data =
	{"pglist_data", pglist_data, "[address]", "print pglist_data (or pg_data_t) table"};

extern void prhead_page();
extern addr_t print_page();
extern addr_t print_vm_area_struct();
extern addr_t print_zone();
extern addr_t print_pglist_data();
extern void print_zone_active_list();
extern addr_t get_zone_from_pglist_data();
extern int sizeof_page;
addr_t mem_map_addr, mem_map;
addr_t pgdat_list_addr;

PRIVATE addr_t
page()
{
	addr_t addr = 0, zaddr, start;
	int i, c;
	int aflag = 0, nflag = 0;

	while ((c = getopt(argcnt, args, "an")) != EOF) {
		switch (c) {
		case 'a':	/* return free list */
			aflag = 1;
			break;
		case 'n':
			nflag = 1;
			break;
		default:
			THROW(usage);
		}
	}

	GETADDR(mem_map);
	if (mem_map == 0) {
		memread(mem_map_addr, sizeof(mem_map), &mem_map, "mem_map");
	}

	if (nflag) {
		if (argcnt == optind)
			THROW(usage);
		prhead_page();
		for (i = optind; i < argcnt; i++) {
			addr = print_page(mem_map + sizeof_page * getvalue(args[i]), 0);
		}
		return addr;
	}

	if (argcnt > optind) {
		prhead_page();
		for (i = optind; i < argcnt; i++) {
			addr = start = getvalue(args[i]);
			do {
				addr = print_page(addr, 0);
			} while (aflag && addr && addr != start);
		}
	} else if (aflag) {
		THROW(usage);
	} else {
		GETADDR(pgdat_list);
		memread(pgdat_list_addr, sizeof(zaddr), &zaddr, "pgdat_list");
		prhead_page();
		for (i = 0; (addr = get_zone_from_pglist_data(zaddr, i)); i++) {
			print_zone_active_list(addr);
		}
		prhead_page();
	}
	return addr;
}

PRIVATE addr_t
vm_area_struct()
{
	int c;
	addr_t addr = 0;
	const int full = 1;

	while ((c = getopt(argcnt, args, "")) != EOF) {
		switch (c) {
		default:
			THROW(usage);
		}
	}

	if (optind == argcnt) {
		THROW(usage);
	}
	while (args[optind]) {
		addr = print_vm_area_struct(getvalue(args[optind]), full);
		optind++;
	}
	return addr;
}

PRIVATE addr_t
zone()
{
	int c, full = 0;
	addr_t addr = 0;
	extern void prhead_zone();

	while ((c = getopt(argcnt, args, "f")) != EOF) {
		switch (c) {
		case 'f':
			full = 1;
			break;
		default:
			THROW(usage);
		}
	}

	if (optind == argcnt) {
		addr_t zaddr;
		int i;

		GETADDR(pgdat_list);
		memread(pgdat_list_addr, sizeof(zaddr), &zaddr, "pgdat_list");
		prhead_zone();
		for (i = 0; (addr = get_zone_from_pglist_data(zaddr, i)); i++) {
			print_zone(addr, full);
		}
		return addr;
	}
	while (args[optind]) {
		addr = print_zone(getvalue(args[optind]), 1);
		optind++;
	}
	return addr;
}

PRIVATE addr_t
pglist_data()
{
	int c;
	addr_t addr = 0;

	while ((c = getopt(argcnt, args, "")) != EOF) {
		switch (c) {
		default:
			THROW(usage);
		}
	}

	if (optind == argcnt) {
		GETADDR(pgdat_list);
		memread(pgdat_list_addr, sizeof(addr), &addr, "pgdat_list");
		addr = print_pglist_data(addr);
	}
	while (args[optind]) {
		addr = print_pglist_data(getvalue(args[optind]));
		optind++;
	}
	return addr;
}
