/*
 * Copyright 1999 Silicon Graphics, Inc. All rights reserved.
 */
#include <klib.h>

/* Some global variables
 */
klib_t *KLP = (klib_t *)NULL;
k_error_t klib_error = 0;
uint64_t klib_errorval;
struct_sizes_t struct_sizes;

/*
 * kl_reset_error()
 */
void
kl_reset_error(void)
{
	klib_error = 0;
}

/*
 * kl_print_error()
 */
void
kl_print_error(void)
{
	int ecode;

	ecode = klib_error & 0xffffffff;
	switch(ecode) {

		/** General klib error codes
		 **/
		case KLE_NO_MEMORY:
			fprintf(KL_ERRORFP, "insufficient memory");
			break;
		case KLE_OPEN_ERROR:
			fprintf(KL_ERRORFP, 
				"unable to open file: %s", sys_errlist[errno]);
			break;
		case KLE_ZERO_BLOCK:
			fprintf(KL_ERRORFP, 
				"tried to allocate a zero-sized block");
			break;
		case KLE_INVALID_VALUE:
			fprintf(KL_ERRORFP, "invalid input value");
			break;
		case KLE_NULL_BUFF:
			fprintf(KL_ERRORFP, "NULL buffer pointer");
			break;
		case KLE_ZERO_SIZE:
			fprintf(KL_ERRORFP, "zero sized block requested");
			break;
		case KLE_ACTIVE:
			fprintf(KL_ERRORFP, 
				"operation not supported on a live system");
			break;
		case KLE_MISC_ERROR:
			fprintf(KL_ERRORFP, "KLIB error");	
			break;
		case KLE_NOT_SUPPORTED:
			fprintf(KL_ERRORFP, "operation not supported");	
			break;
		case KLE_UNKNOWN_ERROR:
			fprintf(KL_ERRORFP, "unknown error");	
			break;

		/** memory error codes
		 **/
		case KLE_BAD_MAP_FILE:
			fprintf(KL_ERRORFP, "bad map file");	
			break;
		case KLE_BAD_DUMP:
			fprintf(KL_ERRORFP, "bad dump file");	
			break;
		case KLE_BAD_DUMPTYPE:
			fprintf(KL_ERRORFP, "bad dumptype");	
			break;
		case KLE_INVALID_LSEEK:
			fprintf(KL_ERRORFP, "lseek error");	
			break;
		case KLE_INVALID_READ:
			fprintf(KL_ERRORFP, "not found in dump file");	
			break;
		case KLE_BAD_MEMINFO:
			fprintf(KL_ERRORFP, "bad meminfo struct");	
			break;
		case KLE_INVALID_PADDR:
			fprintf(KL_ERRORFP, "invalid physical address");	
			break;
		case KLE_INVALID_VADDR:
			fprintf(KL_ERRORFP, "invalid virtual address");	
			break;
		case KLE_INVALID_VADDR_ALIGN:
			fprintf(KL_ERRORFP, "invalid vaddr alignment");	
			break;
		case KLE_INVALID_MAPPING:
			fprintf(KL_ERRORFP, "invalid address mapping");	
			break;

		/** symbol error codes
		 **/
		case KLE_NO_SYMTAB:
			fprintf(KL_ERRORFP, "no symtab");	
			break;
		case KLE_NO_SYMBOLS:
			fprintf(KL_ERRORFP, "no symbol information");	
			break;
		case KLE_NO_MODULE_LIST:
			fprintf(KL_ERRORFP, "kernel without module support");	
			break;

		/** kernel data error codes
		 **/
		case KLE_INVALID_KERNELSTACK:
			fprintf(KL_ERRORFP, "invalid kernel stack");	
			break;
		case KLE_INVALID_STRUCT_SIZE:
			fprintf(KL_ERRORFP, "invalid struct size");	
			break;
		case KLE_BEFORE_RAM_OFFSET:
			fprintf(KL_ERRORFP, 
				"physical address proceeds start of RAM");
			break;
		case KLE_AFTER_MAXPFN:
			fprintf(KL_ERRORFP, "PFN exceeds maximum PFN");
			break;
		case KLE_AFTER_PHYSMEM:
			fprintf(KL_ERRORFP, "address exceeds physical memory");
			break;
		case KLE_AFTER_MAXMEM:
			fprintf(KL_ERRORFP, 
				"address exceeds maximum physical address");
			break;
		case KLE_PHYSMEM_NOT_INSTALLED:
			fprintf(KL_ERRORFP, "physical memory not installed");
			break;
		case KLE_NO_DEFTASK:
			fprintf(KL_ERRORFP, "default task not set");
			break;
		case KLE_PID_NOT_FOUND:
			fprintf(KL_ERRORFP, "PID not found");
			break;
		case KLE_DEFTASK_NOT_ON_CPU:
			fprintf(KL_ERRORFP, 
				"default task not running on a cpu");
			break;
		case KLE_NO_CURCPU:
			fprintf(KL_ERRORFP, 
				"current cpu could not be determined");
			break;

		case KLE_KERNEL_MAGIC_MISMATCH:
			fprintf(KL_ERRORFP, "kernel_magic mismatch "
				"of map and memory image");
			break;

		case KLE_INVALID_DUMP_HEADER:
			fprintf(KL_ERRORFP, "invalid dump header in dump");
			break;

		case KLE_DUMP_INDEX_CREATION:
			fprintf(KL_ERRORFP, "cannot create index file");
			break;

		case KLE_DUMP_HEADER_ONLY:
			fprintf(KL_ERRORFP, "dump only has a dump header");
			break;

		case KLE_NO_END_SYMBOL:
			fprintf(KL_ERRORFP, "no _end symbol in kernel");
			break;

		case KLE_NO_CPU:
			fprintf(KL_ERRORFP, "CPU not installed");
			break;

		default:
			break;
	}	
	fprintf(KL_ERRORFP, "\n");
}

/*
 * alloc_klib()
 */
static klib_t *
alloc_klib(void)
{
	klib_t *klp;

	if ((klp = (klib_t *)malloc(sizeof(klib_t)))) {
		bzero(klp, sizeof(klib_t));
	}
	return(klp);
}

/*
 * kl_free_klib()
 */
void
kl_free_klib(klib_t *klp)
{
	if (klp->k_meminfo) {
		kl_free_meminfo(klp->k_meminfo);
	}
	if (STP) {
		kl_free_syminfo(NULL); /* free complete list, STP points to*/
	}
	free(klp);
}

/*
 * linux_release()
 */
static int
linux_release(void)
{
	int i;
	char *b, *n, revstr[256];
	int revision = 0;
	char *c;
	void *utsname;
	syment_t *sp;

	if (!(sp = kl_lkup_symname("system_utsname"))) {
		return(0);
	}
	utsname = kl_alloc_block(NEW_UTSNAME_SZ, K_TEMP);
	if (KL_ERROR) {
		return(0);
	}
	GET_BLOCK(sp->s_addr, NEW_UTSNAME_SZ, utsname);
	if (KL_ERROR) {
		kl_free_block(utsname);
		return(0);
	}

	/* Try to make sure that we have a valid utsname struct.
	 * If we don't, most likely we are using the wrong map
	 * file. To continue doesn't make sense).
	 */
	c = K_PTR(utsname, "new_utsname", "release");
	if (!(*c)) {
		KL_ERROR = KLE_BAD_MAP_FILE;
		kl_free_block(utsname);
		return(0);
	}
	for (i = 0; i < 256; i++) {
		if (!c[i]) {
			break;
		}
	}
	if (i == 256) {
		KL_ERROR = KLE_BAD_MAP_FILE;
		kl_free_block(utsname);
		return(0);
	}
	strcpy(revstr, K_PTR(utsname, "new_utsname", "release"));
	b = revstr;
	if (!(n = strchr(revstr, '.'))) {
		KL_ERROR = KLE_BAD_MAP_FILE;
		kl_free_block(utsname);
		return(0);
	}
	*n = 0;
	revision |= (atoi(b) << 16);
	b = n + 1;
	if (!(n = strchr(b, '.'))) {
		KL_ERROR = KLE_BAD_MAP_FILE;
		kl_free_block(utsname);
		return(0);
	}
	revision |= (atoi(b) << 8);
	b = n + 1;
	revision |= atoi(b);
	kl_free_block(utsname);
	return(revision);
}

/*
 * init_struct_sizes()
 */
void
init_struct_sizes(void)
{
	TASK_STRUCT_SZ = kl_struct_len("task_struct");
	MM_STRUCT_SZ = kl_struct_len("mm_struct");
	PAGE_SZ = kl_struct_len("page");
	MODULE_SZ = kl_struct_len("module");
	NEW_UTSNAME_SZ = kl_struct_len("new_utsname");
	_DUMP_HEADER_S_SZ = kl_struct_len("_dump_header_s");
	_DUMP_HEADER_ASM_S_SZ = kl_struct_len("_dump_header_asm_s");
}

/*
 * kl_init_klib()
 */
int
kl_init_klib(char *map, char *dump, char *namelist, int flags)
{
	kaddr_t kval;
	syment_t *_end, *kernel_magic;

	if (!(KLP = alloc_klib())) {
		return(1);
	}

	fprintf(KL_ERRORFP, "\n\tInitializing vmdump access ...");
	if (!(MIP = kl_init_meminfo(map, dump, flags))) {
		kl_free_klib(KLP);
		fprintf(KL_ERRORFP, " Failed.");
		return(1);
	} else {
		fprintf(KL_ERRORFP, " Done.");
	}

	fprintf(KL_ERRORFP, "\n\tLoading system map ...");
	if (!(STP = kl_init_syminfo(map, flags))) {
		kl_free_klib(KLP);
		fprintf(KL_ERRORFP, " Failed.");
		return(1);
	} else {
		fprintf(KL_ERRORFP, " Done.");
	}

	/* We don't have to check for success since we can get along
	 * without type information.
	 */
	if (namelist && namelist[0]){
		fprintf(KL_ERRORFP, "\n\tLoading type info (Kerntypes) ...");
		if (kl_open_namelist(namelist, ST_DEFAULT)) {
			fprintf(KL_ERRORFP, " Failed.");
		} else {
			fprintf(KL_ERRORFP, " Done.");
		}
	}
 	init_struct_sizes();

	/* 
	 * kl_arch_init() can only be called after struct_sizes is initialized,
	 *                and assumes MIP has be set.
	 */
	fprintf(KL_ERRORFP, "\n\tInitializing arch specific data ...");
	if (kl_arch_init()) {
		kl_free_klib(KLP);
                fprintf(KL_ERRORFP, " Failed.");
                return(1);
	} else {
                fprintf(KL_ERRORFP, " Done.");
        }

	/* Make sure the kernel_magic and _end flags match up, if
	 * they exist.
	 */
	if (!(kernel_magic = kl_lkup_symname("kernel_magic"))) {
		/* ignore -- check isn't in this kernel */
		KL_ERROR = 0;
	} else {
		/* we can't ignore this failure -- it's fatal */
		if (!(_end = kl_lkup_symname("_end"))) {
			KL_ERROR = KLE_NO_END_SYMBOL;
			kl_free_klib(KLP);
			return(1);
		}

		/* if the value doesn't exist, we're screwed */
		if (!(kval = kaddr_to_ptr(kernel_magic->s_addr))) {
			kl_free_klib(KLP);
			return(1);
		}

		/* create an option to ignore this in the future */
		if (_end->s_addr != kval) {
			KL_ERROR = KLE_KERNEL_MAGIC_MISMATCH;
			kl_free_klib(KLP);
			return(1);
		}
	}

 	if (!(KL_LINUX_RELEASE = linux_release())) {
 		kl_free_klib(KLP);
 		return(1);
 	}

	fprintf(KL_ERRORFP, "\n\tLoading ksyms from dump ...");
	if (kl_init_ksyms(0)){
		fprintf(KL_ERRORFP, " Failed.");
		if(KL_ERROR == KLE_NO_MODULE_LIST) {
			fprintf(KL_ERRORFP,"\n \t\tReason: ");
			kl_print_error();
			kl_reset_error();
		}
	} else {
		fprintf(KL_ERRORFP, " Done.");
	}

	return(0);
}
