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

#define BYTES_PER_LINE 16

/*
 * task_has_cpu(tsp): Return 1 if task 'tsp' is running on a cpu else return 0
 */
int task_has_cpu(void *tsp)
{
	if(KL_LINUX_RELEASE > LINUX_2_4_15){
		unsigned long cpus_runnable;
		cpus_runnable = KL_UINT(tsp,"task_struct","cpus_runnable");
		if(cpus_runnable != ~0UL)
			return 1;
		else
			return 0;
	} else {
		if(KL_INT(tsp,"task_struct","has_cpu"))
			return 1;
		else
			return 0;
	}
}

/*
 * get_utsname()
 */
char *
get_utsname(void)
{
	char *utsname;
	syment_t *sp;

	if (!(sp = kl_lkup_symname("system_utsname"))) {
		return(0);
	}
	utsname = (char *)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);
        }

	return(utsname);
}

/*
 * dump_memory()
 */
void
dump_memory(kaddr_t addr, uint64_t count, int flags, FILE *ofp)
{
	int i, j, type, base, nbytes, remaining = count;
	int nunits, unit_size; 
	int units_per_line, units_per_blk, lines_per_entry = 1;
	int capture_ascii = 0;
	kaddr_t cur_addr;
	void *blk, *ptr;
	char *cp, cstr[17];

	if (flags & C_INSTR) {
		/* Dump out count instructions starting with addr. We
		 * have to make a call to an architecture specific 
		 * function to do this for us.
		 */
		dump_instr(addr, count, flags, ofp);
		return;
	}

	/* Make sure there are no conflicts with the flags controlling
	 * the base (octal, hex, decimal) and element size (byte, half
	 * word, word and double word). Set base and ewidth to the highest
	 * priority flag that is set.
	 */
	if (flags & C_HEX) {
		base = C_HEX;
	} else if (flags & C_DECIMAL) {
		base = C_DECIMAL;
	} else if (flags & C_OCTAL) {
		base = C_OCTAL;
	} else {
		base = C_HEX;
	}

	/* If none of the width flags are set, use the pointer size to
	 * determine what the width should be.
	 */
	if (!(flags & C_DWORD) && !(flags & C_WORD) &&
		!(flags & C_HWORD) && !(flags & C_BYTE)) {
		if (PTRSZ64) {
			flags |= C_DWORD;
		} else {
			flags |= C_WORD;
		}
	}

	/* Make sure that addr is aligned properly (based on flag values).
	 */
	if (flags & C_BYTE) {
		type = C_BYTE;
		units_per_blk = BLKSZ;
		units_per_line = 16;
		unit_size = 1;
		if ((base == C_DECIMAL) || (base == C_OCTAL)) {
			lines_per_entry = 2; 
		}
	} else if (flags & C_HWORD) {
		type = C_HWORD;
		if (addr % 2) {
			fprintf(KL_ERRORFP, "addr (0x%"FMTPTR"x) is not "
				"half_word aligned!\n", addr);
			return;
		}
		units_per_blk = BLKSZ / 2;
		units_per_line = 8;
		unit_size = 2;
	} else if (flags & C_WORD) {
		type = C_WORD;
		if (addr % 4) {
			fprintf(KL_ERRORFP, "addr (0x%"FMTPTR"x) is not "
				"word aligned!\n", addr);
			return;
		}
		units_per_blk = BLKSZ / 4;
		units_per_line = 4;
		unit_size = 4;
		if (base == C_OCTAL) {
			lines_per_entry = 2;
		}
	} else if (flags & C_DWORD) {
		type = C_DWORD;
		if (addr % 8) {
			fprintf(KL_ERRORFP, "addr (0x%"FMTPTR"x) is not "
				"double word aligned!\n", addr);
			return;
		}
		units_per_blk = BLKSZ / 8;
		units_per_line = 2;
		unit_size = 8;
	} 

	/* Turn on ASCII dump only if outputtting in HEX 
	 */
	if (base == C_HEX) {
		capture_ascii = 1;
	}

	if (!(blk = (void *)kl_alloc_block(BLKSZ, K_TEMP))) {
		return;
	}
	bzero(blk, BLKSZ);

	cur_addr = addr;
	while (remaining) {

		if (remaining >= units_per_blk) {
			nbytes = BLKSZ;
			nunits = units_per_blk;
			remaining -= units_per_blk;
		} else {
			nbytes = remaining * unit_size;
			nunits = remaining;
			remaining = 0;
		}

		if (flags & C_PADDR)
			kl_readmem(cur_addr, nbytes, blk);
		else
			GET_BLOCK(cur_addr, nbytes, blk);
		if (KL_ERROR) {
			fprintf(KL_ERRORFP, "dump_memory: error=%"FMT64"d\n", 
				klib_error);
			break;
		}

		ptr = blk;
		i = 0;
		while (i < nunits) {
			if ((i % units_per_line) == 0) {
				print_kaddr(cur_addr, ofp, 0);
				fprintf(ofp, ": ");
				cp = ptr;
				cur_addr += BYTES_PER_LINE;
			}
			if (type == C_BYTE) {
				switch (base) {
					case C_HEX:
						fprintf(ofp, "%02x ", 
						*(unsigned char*)ptr);
						break;

					case C_DECIMAL:
						fprintf(ofp, "%04d ", 
						*(unsigned char*)ptr);
						break;

					case C_OCTAL:
						fprintf(ofp, "%04o ", 
						*(unsigned char*)ptr);
						break;
				}
				ptr++;
			} else if (type == C_HWORD) {
				switch (base) {
					case C_HEX:
						fprintf(ofp, "%04x ", 
						*(unsigned short*)ptr);
						break;

					case C_DECIMAL:
						fprintf(ofp, "%06d ", 
						*(unsigned short*)ptr);
						break;

					case C_OCTAL:
						fprintf(ofp, "%07o ", 
						*(unsigned short*)ptr);
						break;
				}
				ptr += 2;
			} else if (type == C_WORD) {
				switch (base) {
					case C_HEX:
						fprintf(ofp, "%08x ", 
						*(unsigned int*)ptr);
						break;

					case C_DECIMAL:
						fprintf(ofp, "%011d ", 
						*(unsigned int*)ptr);
						break;

					case C_OCTAL:
						fprintf(ofp, "%020o ", 
						*(unsigned int*)ptr);
						break;
				}
				ptr += 4;
			} else if (type == C_DWORD) {
				switch (base) {
					case C_HEX:
						fprintf(ofp, "%016llx ", 
						*(unsigned long long*)ptr);
						break;

					case C_DECIMAL:
						fprintf(ofp, "%020lld ", 
						*(unsigned long long*)ptr);
						break;

					case C_OCTAL:
						fprintf(ofp, "%023llo ", 
						*(unsigned long long*)ptr);
						break;
				}
				ptr += 8;
			}
			i++;
			if ((i < nunits) && (lines_per_entry == 2) && 
			    ((i % units_per_line) == (units_per_line/2))) {
				if (PTRSZ32) {
					fprintf(ofp, "\n            ");
				} else {
					fprintf(ofp, "\n                    ");
				}
			}
			if (capture_ascii &&
				((i && ((i % units_per_line) == 0)) ||
						(i == nunits))) {

				int extra = 0;

				if ((i == nunits) && (i % units_per_line)) {
					/* Our last line is not a full one. 
					 * We have to padd it with blanks.
					 */
					extra = units_per_line -
						(i % units_per_line);
					for (j = 0; j < extra; j++) {
						if (type == C_BYTE) {
							fprintf(ofp, "   "); 	
						} else if (type == C_HWORD) {
							fprintf(ofp, "     "); 	
						} else if (type == C_WORD) {
							fprintf(ofp, 
								"         "); 	
						} else {
							fprintf(ofp, "      "
								"           ");
						}
					}
				}
				
				/* Get an ASCII representation of 
				 * the bytes being dumpped
				 */
				for(j = 0; j < 16 - (extra * unit_size); j++) {
					if (*cp >= 32 && *cp <= 126) {
						cstr[j] = (char)*cp;
					} else {
						cstr[j] = '.';
					}
					cp++;
				}
				cstr[j] = 0;
				fprintf(ofp, ": %s\n", cstr);
			} else if (i && (((i % units_per_line) == 0) ||
					(i == nunits))) {
				fprintf(ofp, "\n");
			}
		}
	}
	kl_free_block(blk);
}

int
i_fprintf(int *ofp, const char *fmt, char *buffer) 
{
	return(fprintf((FILE *)ofp, fmt, buffer));
}


/*
 * kl_block_alloc_func()
 */
void *
kl_block_alloc_func(int size, int flag, void *ra)
{
	void *b;

	b = (void *)alloc_block(size, flag, ra);
	return(b);
}

/*
 * kl_block_realloc_func()
 */
void *
kl_block_realloc_func(void *blk, int size, int flag, void *ra)
{
	void *b;

	b = (void *)realloc_block(blk, size, flag, ra);
	return(b);
}

/*
 * kl_block_dup_func()
 */
void *
kl_block_dup_func(void *blk, int flag, void *ra)
{
	void *b;

	b = (void *)dup_block(blk, flag, ra);
	return(b);
}

/*
 * kl_str_to_block_func()
 */
void *
kl_str_to_block_func(char * s, int flag, void *ra)
{
	void *b;

	b = (void *)str_to_block(s, flag, ra);
	return(b);
}

/*
 * kl_block_free_func()
 */
void
kl_block_free_func(void *blk)
{
	free_block(blk);
}

/*
 * init_liballoc()
 *
 * Provides a wrapper around the init_mempool() function from liballoc.
 */
void
init_liballoc(int pghdr_cnt, int blkhdr_cnt, int page_cnt)
{
	init_mempool(pghdr_cnt, blkhdr_cnt, page_cnt);
	KL_BLOCK_ALLOC() = kl_block_alloc_func;
	KL_BLOCK_REALLOC() = kl_block_realloc_func;
	KL_BLOCK_DUP() = kl_block_dup_func;
	KL_STR_TO_BLOCK() = kl_str_to_block_func;
	KL_BLOCK_FREE() = kl_block_free_func;
}

/*
 * get_string()
 */
int
get_string(char *str, int size)
{
	char c;
	int count = 0;

	if (!str || (size == 0)) {
		return(0);	
	}
	while ((c = getchar())) {
		/* If we're at the end of the line, return without updating
		 * the string.
		 */
		if (c == '\n') {
			break;
		}

		str[count] = c;
		count++;
		/* See if we have a full string. Make sure we leave 
		 * room for the terminating NULL
		 */
		if (count == (size - 1)) {
			break;
		}
	}
	str[count] = 0;
	return(count);
}

/*
 * symbol_banner()
 */
void
symbol_banner(FILE *ofp, int flags)
{
        print_banner(ofp, "%      ADDR%>  OFFSET  TYPE         NAME%}60",
		     flags);
}

/* 
 * print_symbol()
 */
void
print_symbol(kaddr_t pc, syment_t *sp, int flags, FILE *ofp)
{
	int offset;
	kaddr_t addr;

	if (!sp) {
		/* XXX -- Set error */
		return;
	}

	if (pc) {
		addr = pc;	
	} else {
		addr = sp->s_addr;
	}
	print_value(ofp, "", ADDR64(addr), 10, ADDR_FLG);
	offset = (addr - sp->s_addr);
	fprintf(ofp, "  %6d  ", offset);
	switch(sp->s_type) {
		case SYM_GLOBAL_TEXT:
			fprintf(ofp, "GLOBAL_TEXT  ");
			break;
		case SYM_LOCAL_TEXT:
			fprintf(ofp, "LOCAL_TEXT   ");
			break;
		case SYM_LOCORE_TEXT:
			fprintf(ofp, "LOCORE_TEXT  ");
			break;
		case SYM_GLOBAL_DATA:
			fprintf(ofp, "GLOBAL_DATA  ");
			break;
		case SYM_LOCAL_DATA:
			fprintf(ofp, "LOCAL_DATA   ");
			break;
		case SYM_ABS:
			fprintf(ofp, "ABS          ");
			break;
		case SYM_KSYM:
			fprintf(ofp, "EXP_KSYM     ");
			break;
		case SYM_UNK:
		default:
			fprintf(ofp, "UNKNOWN      ");
			break;
	}
	fprintf(ofp, "%s\n", sp->s_name);
}
