/*
 * util.c: Commonly used utility functions.
 *
 * Leendert van Doorn, leendert@watson.ibm.com
 * Copyright (c) 2005, International Business Machines Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place - Suite 330, Boston, MA 02111-1307 USA.
 */
#include <stdarg.h>

#include "util.h"
#include "machine.h"

#define	isdigit(c)	((c) >= '0' && (c) <= '9')
#define	min(a, b)	((a) < (b) ? (a) : (b))

static void putchar(int);
static char *printnum(char *, unsigned long, int);
static void _doprint(void (*)(int), char const *, va_list);

void
cpuid_addr_value(uint64_t addr, uint64_t *value)
{
	uint32_t addr_low   = (uint32_t)addr;
	uint32_t addr_high  = (uint32_t)(addr >> 32);
	uint32_t value_low, value_high;
	static unsigned int addr_leaf;

	if (!addr_leaf) {
		unsigned int eax, ebx, ecx, edx;
		__asm__ __volatile__(
			"cpuid"
			: "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)
			: "0" (0x40000000));
		addr_leaf = eax + 1;
	}

	__asm__ __volatile__(
		"cpuid"
		: "=c" (value_low), "=d" (value_high)
		: "a" (addr_leaf), "0" (addr_low), "1" (addr_high)
		: "ebx");

	*value = (uint64_t)value_high << 32 | value_low;
}

void
dump_regs(struct regs *regs)
{
	printf("eax    %8x ecx    %8x edx    %8x ebx    %8x\n",
		regs->eax, regs->ecx, regs->edx, regs->ebx);
	printf("esp    %8x ebp    %8x esi    %8x edi    %8x\n",
		regs->esp, regs->ebp, regs->esi, regs->edi);
	printf("trapno %8x errno  %8x\n", regs->trapno, regs->errno);
	printf("eip    %8x cs     %8x eflags %8x\n",
		regs->eip, regs->cs, regs->eflags);
	printf("uesp   %8x uss    %8x\n",
		regs->uesp, regs->uss);
	printf("ves    %8x vds    %8x vfs    %8x vgs    %8x\n",
		regs->ves, regs->vds, regs->vfs, regs->vgs);

	printf("cr0    %8lx cr2    %8x cr3    %8lx cr4    %8lx\n\n",
		(long)oldctx.cr0, get_cr2(),
		(long)oldctx.cr3, (long)oldctx.cr4);
}

#ifdef DEBUG
void
hexdump(unsigned char *data, int sz)
{
	unsigned char *d;
	int i;

	for (d = data; sz > 0; d += 16, sz -= 16) {
		int n = sz > 16 ? 16 : sz;

		printf("%08x: ", (unsigned)d);
		for (i = 0; i < n; i++)
			printf("%02x%c", d[i], i == 7 ? '-' : ' ');
		for (; i < 16; i++)
			printf("  %c", i == 7 ? '-' : ' ');
		printf("   ");
		for (i = 0; i < n; i++)
			printf("%c", d[i] >= ' ' && d[i] <= '~' ? d[i] : '.');
		printf("\n");
	}
}

void
print_e820_map(struct e820entry *map, int entries)
{
	struct e820entry *m;

	if (entries > 32)
		entries = 32;

	for (m = map; m < &map[entries]; m++) {
		printf("%08lx%08lx - %08lx%08lx ",
			(unsigned long) (m->addr >> 32),
			(unsigned long) (m->addr),
			(unsigned long) ((m->addr+m->size) >> 32),
			(unsigned long) ((m->addr+m->size)));

		switch (m->type) {
		case E820_RAM:
			printf("(RAM)\n"); break;
		case E820_RESERVED:
			printf("(Reserved)\n"); break;
		case E820_ACPI:
			printf("(ACPI Data)\n"); break;
		case E820_NVS:
			printf("(ACPI NVS)\n"); break;
		default:
			printf("(Type %ld)\n", m->type); break;
		}
	}
}

void
dump_dtr(unsigned long addr, unsigned long size)
{
	unsigned long long entry;
	unsigned long base, limit;
	int i;

	for (i = 0; i < size; i += 8) {
		entry = ((unsigned long long *) addr)[i >> 3];
		base = (((entry >> (56-24)) & 0xFF000000) |
			((entry >> (32-16)) & 0x00FF0000) |
			((entry >> (   16)) & 0x0000FFFF));
		limit = (((entry >> (48-16)) & 0x000F0000) |
		         ((entry           ) & 0x0000FFFF));
		if (entry & (1ULL << (23+32))) /* G */
			limit = (limit << 12) | 0xFFF;

		printf("[0x%x] = 0x%08x%08x, base 0x%lx, limit 0x%lx\n", i,
			(unsigned)(entry >> 32), (unsigned)(entry),
			base, limit);
	}
}

void
dump_vmx_context(struct vmx_assist_context *c)
{
	printf("eip 0x%lx, esp 0x%lx, eflags 0x%lx\n",
		(long) c->eip, (long) c->esp, (long) c->eflags);

	printf("cr0 0x%lx, cr3 0x%lx, cr4 0x%lx\n",
		(long)c->cr0, (long)c->cr3, (long)c->cr4);

	printf("idtr: limit 0x%lx, base 0x%lx\n",
		(long)c->idtr_limit, (long)c->idtr_base);

	printf("gdtr: limit 0x%lx, base 0x%lx\n",
		(long)c->gdtr_limit, (long)c->gdtr_base);

	printf("cs: sel 0x%lx, limit 0x%lx, base 0x%lx\n",
		(long)c->cs_sel, (long)c->cs_limit, (long)c->cs_base);
	printf("\ttype %d, s %d, dpl %d, p %d, avl %d, ops %d, g %d, nul %d\n",
		c->cs_arbytes.fields.seg_type,
		c->cs_arbytes.fields.s,
		c->cs_arbytes.fields.dpl,
		c->cs_arbytes.fields.p,
		c->cs_arbytes.fields.avl,
		c->cs_arbytes.fields.default_ops_size,
		c->cs_arbytes.fields.g,
		c->cs_arbytes.fields.null_bit);

	printf("ds: sel 0x%lx, limit 0x%lx, base 0x%lx\n",
		(long)c->ds_sel, (long)c->ds_limit, (long)c->ds_base);
	printf("\ttype %d, s %d, dpl %d, p %d, avl %d, ops %d, g %d, nul %d\n",
		c->ds_arbytes.fields.seg_type,
		c->ds_arbytes.fields.s,
		c->ds_arbytes.fields.dpl,
		c->ds_arbytes.fields.p,
		c->ds_arbytes.fields.avl,
		c->ds_arbytes.fields.default_ops_size,
		c->ds_arbytes.fields.g,
		c->ds_arbytes.fields.null_bit);

	printf("es: sel 0x%lx, limit 0x%lx, base 0x%lx\n",
		(long)c->es_sel, (long)c->es_limit, (long)c->es_base);
	printf("\ttype %d, s %d, dpl %d, p %d, avl %d, ops %d, g %d, nul %d\n",
		c->es_arbytes.fields.seg_type,
		c->es_arbytes.fields.s,
		c->es_arbytes.fields.dpl,
		c->es_arbytes.fields.p,
		c->es_arbytes.fields.avl,
		c->es_arbytes.fields.default_ops_size,
		c->es_arbytes.fields.g,
		c->es_arbytes.fields.null_bit);

	printf("ss: sel 0x%lx, limit 0x%lx, base 0x%lx\n",
		(long)c->ss_sel, (long)c->ss_limit, (long)c->ss_base);
	printf("\ttype %d, s %d, dpl %d, p %d, avl %d, ops %d, g %d, nul %d\n",
		c->ss_arbytes.fields.seg_type,
		c->ss_arbytes.fields.s,
		c->ss_arbytes.fields.dpl,
		c->ss_arbytes.fields.p,
		c->ss_arbytes.fields.avl,
		c->ss_arbytes.fields.default_ops_size,
		c->ss_arbytes.fields.g,
		c->ss_arbytes.fields.null_bit);

	printf("fs: sel 0x%lx, limit 0x%lx, base 0x%lx\n",
		(long)c->fs_sel, (long)c->fs_limit, (long)c->fs_base);
	printf("\ttype %d, s %d, dpl %d, p %d, avl %d, ops %d, g %d, nul %d\n",
		c->fs_arbytes.fields.seg_type,
		c->fs_arbytes.fields.s,
		c->fs_arbytes.fields.dpl,
		c->fs_arbytes.fields.p,
		c->fs_arbytes.fields.avl,
		c->fs_arbytes.fields.default_ops_size,
		c->fs_arbytes.fields.g,
		c->fs_arbytes.fields.null_bit);

	printf("gs: sel 0x%lx, limit 0x%lx, base 0x%lx\n",
		(long)c->gs_sel, (long)c->gs_limit, (long)c->gs_base);
	printf("\ttype %d, s %d, dpl %d, p %d, avl %d, ops %d, g %d, nul %d\n",
		c->gs_arbytes.fields.seg_type,
		c->gs_arbytes.fields.s,
		c->gs_arbytes.fields.dpl,
		c->gs_arbytes.fields.p,
		c->gs_arbytes.fields.avl,
		c->gs_arbytes.fields.default_ops_size,
		c->gs_arbytes.fields.g,
		c->gs_arbytes.fields.null_bit);

	printf("tr: sel 0x%lx, limit 0x%lx, base 0x%lx\n",
		(long)c->tr_sel, (long)c->tr_limit, (long)c->tr_base);
	printf("\ttype %d, s %d, dpl %d, p %d, avl %d, ops %d, g %d, nul %d\n",
		c->tr_arbytes.fields.seg_type,
		c->tr_arbytes.fields.s,
		c->tr_arbytes.fields.dpl,
		c->tr_arbytes.fields.p,
		c->tr_arbytes.fields.avl,
		c->tr_arbytes.fields.default_ops_size,
		c->tr_arbytes.fields.g,
		c->tr_arbytes.fields.null_bit);

	printf("ldtr: sel 0x%lx, limit 0x%lx, base 0x%lx\n",
		(long)c->ldtr_sel, (long)c->ldtr_limit, (long)c->ldtr_base);
	printf("\ttype %d, s %d, dpl %d, p %d, avl %d, ops %d, g %d, nul %d\n",
		c->ldtr_arbytes.fields.seg_type,
		c->ldtr_arbytes.fields.s,
		c->ldtr_arbytes.fields.dpl,
		c->ldtr_arbytes.fields.p,
		c->ldtr_arbytes.fields.avl,
		c->ldtr_arbytes.fields.default_ops_size,
		c->ldtr_arbytes.fields.g,
		c->ldtr_arbytes.fields.null_bit);

	printf("GDTR <0x%lx,0x%lx>:\n",
		(long)c->gdtr_base, (long)c->gdtr_limit);
	dump_dtr(c->gdtr_base, c->gdtr_limit);
}
#endif /* DEBUG */

/*
 * Lightweight printf that doesn't drag in everything under the sun.
 */
int
printf(const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	_doprint(putchar, fmt, ap);
	va_end(ap);
	return 0; /* for gcc compat */
}

int
vprintf(const char *fmt, va_list ap)
{
	_doprint(putchar, fmt, ap);
	return 0; /* for gcc compat */
}

void
panic(const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	_doprint(putchar, fmt, ap);
	putchar('\n');
	va_end(ap);
	halt();
}

unsigned
strlen(const char *s)
{
	const char *q = s;

	while (*s++)
		/* void */;
	return s - q - 1;
}

static void
putchar(int ch)
{
	outb(0xE9, ch);
}

/*
 * A stripped down version of doprint,
 * but still powerful enough for most tasks.
 */
static void
_doprint(void (*put)(int), char const *fmt, va_list ap)
{
	register char *str, c;
	int lflag, zflag, nflag;
	char buffer[17];
	unsigned value;
	int i, slen, pad;

	for ( ; *fmt != '\0'; fmt++) {
		pad = zflag = nflag = lflag = 0;
		if (*fmt == '%') {
			c = *++fmt;
			if (c == '-' || isdigit(c)) {
				if (c == '-') {
					nflag = 1;
					c = *++fmt;
				}
				zflag = c == '0';
				for (pad = 0; isdigit(c); c = *++fmt)
					pad = (pad * 10) + c - '0';
			}
			if (c == 'l') { /* long extension */
				lflag = 1;
				c = *++fmt;
			}
			if (c == 'd' || c == 'u' || c == 'o' || c == 'x') {
				if (lflag)
					value = va_arg(ap, unsigned);
				else
					value = (unsigned) va_arg(ap, unsigned int);
				str = buffer;
				printnum(str, value,
					c == 'o' ? 8 : (c == 'x' ? 16 : 10));
				goto printn;
			} else if (c == 'O' || c == 'D' || c == 'X') {
				value = va_arg(ap, unsigned);
				str = buffer;
				printnum(str, value,
					c == 'O' ? 8 : (c == 'X' ? 16 : 10));
			printn:
				slen = strlen(str);
				for (i = pad - slen; i > 0; i--)
					put(zflag ? '0' : ' ');
				while (*str) put(*str++);
			} else if (c == 's') {
				str = va_arg(ap, char *);
				slen = strlen(str);
				if (nflag == 0)
					for (i = pad - slen; i > 0; i--) put(' ');
				while (*str) put(*str++);
				if (nflag)
					for (i = pad - slen; i > 0; i--) put(' ');
			} else if (c == 'c')
				put(va_arg(ap, int));
			else
				put(*fmt);
		} else
			put(*fmt);
	}
}

static char *
printnum(char *p, unsigned long num, int base)
{
	unsigned long n;

	if ((n = num/base) > 0)
		p = printnum(p, n, base);
	*p++ = "0123456789ABCDEF"[(int)(num % base)];
	*p = '\0';
	return p;
}

void *
memset(void *s, int c, unsigned n)
{
        int t0, t1;

        __asm__ __volatile__ ("cld; rep; stosb"
                : "=&c" (t0), "=&D" (t1)
                : "a" (c), "1" (s), "0" (n)
                : "memory");
        return s;
}

void *
memcpy(void *dest, const void *src, unsigned n)
{
	int t0, t1, t2;

	__asm__ __volatile__(
		"cld\n"
		"rep; movsl\n"
		"testb $2,%b4\n"
		"je 1f\n"
		"movsw\n"
		"1: testb $1,%b4\n"
		"je 2f\n"
		"movsb\n"
		"2:"
		: "=&c" (t0), "=&D" (t1), "=&S" (t2)
		: "0" (n/4), "q" (n), "1" ((long) dest), "2" ((long) src)
		: "memory"
	);
	return dest;
}

