#include <stdarg.h>
#include <limits.h>
#include "arch/io.h"
#include "stdint.h"
#include "uniform_boot.h"
#include "linuxbios_tables.h"
#include "elf_boot.h"
#include "convert.h"
#define STACK_SIZE (4096)

#define __unused __attribute__ ((unused))

long user_stack [STACK_SIZE] = { 0 };

unsigned long * stack_start = & user_stack[STACK_SIZE];

/* FIXME expand on drive_info_)struct... */
struct drive_info_struct {
	uint8_t dummy[32];
};
struct sys_desc_table {
	uint16_t length;
	uint8_t  table[318];
};

/*
 * These are set up by the setup-routine at boot-time:
 */

struct screen_info {
	uint8_t  orig_x;		/* 0x00 */
	uint8_t  orig_y;		/* 0x01 */
	uint16_t dontuse1;		/* 0x02 -- EXT_MEM_K sits here */
	uint16_t orig_video_page;	/* 0x04 */
	uint8_t  orig_video_mode;	/* 0x06 */
	uint8_t  orig_video_cols;	/* 0x07 */
	uint16_t unused2;		/* 0x08 */
	uint16_t orig_video_ega_bx;	/* 0x0a */
	uint16_t unused3;		/* 0x0c */
	uint8_t	 orig_video_lines;	/* 0x0e */
	uint8_t	 orig_video_isVGA;	/* 0x0f */
	uint16_t orig_video_points;	/* 0x10 */

	/* VESA graphic mode -- linear frame buffer */
	uint16_t lfb_width;		/* 0x12 */
	uint16_t lfb_height;		/* 0x14 */
	uint16_t lfb_depth;		/* 0x16 */
	uint32_t lfb_base;		/* 0x18 */
	uint32_t lfb_size;		/* 0x1c */
	uint16_t dontuse2, dontuse3;	/* 0x20 -- CL_MAGIC and CL_OFFSET here */
	uint16_t lfb_linelength;	/* 0x24 */
	uint8_t	 red_size;		/* 0x26 */
	uint8_t	 red_pos;		/* 0x27 */
	uint8_t	 green_size;		/* 0x28 */
	uint8_t	 green_pos;		/* 0x29 */
	uint8_t	 blue_size;		/* 0x2a */
	uint8_t	 blue_pos;		/* 0x2b */
	uint8_t	 rsvd_size;		/* 0x2c */
	uint8_t	 rsvd_pos;		/* 0x2d */
	uint16_t vesapm_seg;		/* 0x2e */
	uint16_t vesapm_off;		/* 0x30 */
	uint16_t pages;			/* 0x32 */
};


#define PAGE_SIZE 4096


#define E820MAP	0x2d0		/* our map */
#define E820MAX	32		/* number of entries in E820MAP */
#define E820NR	0x1e8		/* # entries in E820MAP */


struct e820entry {
	unsigned long long addr;	/* start of memory segment */
	unsigned long long size;	/* size of memory segment */
	unsigned long type;		/* type of memory segment */
#define E820_RAM	1
#define E820_RESERVED	2
#define E820_ACPI	3 /* usable as RAM once ACPI tables have been read */
#define E820_NVS	4
};

struct e820map {
    int nr_map;
	struct e820entry map[E820MAX];
};


struct apm_bios_info {
	uint16_t version;       /* 0x40 */
	uint16_t cseg;		/* 0x42 */
	uint32_t offset;	/* 0x44 */
	uint16_t cseg_16;	/* 0x48 */
	uint16_t dseg;		/* 0x4a */
	uint16_t flags;		/* 0x4c */
	uint16_t cseg_len;	/* 0x4e */
	uint16_t cseg_16_len;	/* 0x50 */
	uint16_t dseg_len;	/* 0x52 */
	uint8_t  reserved[44];	/* 0x54 */
};


struct parameters {
	uint8_t  orig_x;			/* 0x00 */
	uint8_t  orig_y;			/* 0x01 */
	uint16_t ext_mem_k;			/* 0x02 -- EXT_MEM_K sits here */
	uint16_t orig_video_page;		/* 0x04 */
	uint8_t  orig_video_mode;		/* 0x06 */
	uint8_t  orig_video_cols;		/* 0x07 */
	uint16_t unused2;			/* 0x08 */
	uint16_t orig_video_ega_bx;		/* 0x0a */
	uint16_t unused3;			/* 0x0c */
	uint8_t	 orig_video_lines;		/* 0x0e */
	uint8_t	 orig_video_isVGA;		/* 0x0f */
	uint16_t orig_video_points;		/* 0x10 */

	/* VESA graphic mode -- linear frame buffer */
	uint16_t lfb_width;			/* 0x12 */
	uint16_t lfb_height;			/* 0x14 */
	uint16_t lfb_depth;			/* 0x16 */
	uint32_t lfb_base;			/* 0x18 */
	uint32_t lfb_size;			/* 0x1c */
	uint16_t cl_magic;			/* 0x20 */
#define CL_MAGIC_VALUE 0xA33F
	uint16_t cl_offset;			/* 0x22 */
	uint16_t lfb_linelength;		/* 0x24 */
	uint8_t	 red_size;			/* 0x26 */
	uint8_t	 red_pos;			/* 0x27 */
	uint8_t	 green_size;			/* 0x28 */
	uint8_t	 green_pos;			/* 0x29 */
	uint8_t	 blue_size;			/* 0x2a */
	uint8_t	 blue_pos;			/* 0x2b */
	uint8_t	 rsvd_size;			/* 0x2c */
	uint8_t	 rsvd_pos;			/* 0x2d */
	uint16_t vesapm_seg;			/* 0x2e */
	uint16_t vesapm_off;			/* 0x30 */
	uint16_t pages;				/* 0x32 */
	uint8_t  reserved4[12];			/* 0x34 -- 0x3f reserved for future expansion */

	struct apm_bios_info apm_bios_info;	/* 0x40 */
	struct drive_info_struct drive_info;	/* 0x80 */
	struct sys_desc_table sys_desc_table;	/* 0xa0 */
	uint32_t alt_mem_k;			/* 0x1e0 */
	uint8_t  reserved5[4];			/* 0x1e4 */
	uint8_t  e820_map_nr;			/* 0x1e8 */
	uint8_t  reserved6[9];			/* 0x1e9 */
	uint16_t mount_root_rdonly;		/* 0x1f2 */
	uint8_t  reserved7[4];			/* 0x1f4 */
	uint16_t ramdisk_flags;			/* 0x1f8 */
#define RAMDISK_IMAGE_START_MASK  	0x07FF
#define RAMDISK_PROMPT_FLAG		0x8000
#define RAMDISK_LOAD_FLAG		0x4000
	uint8_t  reserved8[2];			/* 0x1fa */
	uint16_t orig_root_dev;			/* 0x1fc */
	uint8_t  reserved9[1];			/* 0x1fe */
	uint8_t  aux_device_info;		/* 0x1ff */
	uint8_t  reserved10[2];			/* 0x200 */
	uint8_t  param_block_signature[4];	/* 0x202 */
	uint16_t param_block_version;		/* 0x206 */
	uint8_t  reserved11[8];			/* 0x208 */
	uint8_t  loader_type;			/* 0x210 */
#define LOADER_TYPE_LOADLIN         1
#define LOADER_TYPE_BOOTSECT_LOADER 2
#define LOADER_TYPE_SYSLINUX        3
#define LOADER_TYPE_ETHERBOOT       4
#define LOADER_TYPE_KERNEL          5
	uint8_t  loader_flags;			/* 0x211 */
	uint8_t  reserved12[2];			/* 0x212 */
	uint32_t kernel_start;			/* 0x214 */
	uint32_t initrd_start;			/* 0x218 */
	uint32_t initrd_size;			/* 0x21c */
        uint8_t  reserved13[4];                 /* 0x220 */
        /* 2.01+ */
        uint16_t heap_end_ptr;                  /* 0x224 */
        uint8_t  reserved14[2];                 /* 0x226 */
        /* 2.02+ */
        uint32_t cmd_line_ptr;                  /* 0x228 */
        /* 2.03+ */
        uint32_t initrd_addr_max;               /* 0x22c */
	/* 2.05+ */
	uint32_t kernel_alignment;		/* 0x230 */
	uint8_t  relocateable_kernel;		/* 0x234 */
	uint8_t  reserved15[0x2d0 - 0x235];     /* 0x235 */

	struct e820entry e820_map[E820MAX];	/* 0x2d0 */
	uint8_t  reserved16[688];		/* 0x550 */
#define COMMAND_LINE_SIZE 256
	uint8_t  command_line[COMMAND_LINE_SIZE]; /* 0x800 */
	uint8_t  reserved17[1792];		/* 0x900 - 0x1000 */
};

/* Keep track of which information I need to acquire. */
struct param_info {
	unsigned type;
	void *data;
	Elf_Bhdr *param;
	struct image_parameters *image;
	struct parameters *real_mode;
	/* bootloader type */
	int has_multiboot;
	int has_uniform_boot;
	int has_elf_boot;
	/* firmware type */
	int has_pcbios;
	int has_linuxbios;
	struct lb_header *lb_table;
	/* machine information needed */
	int need_mem_sizes;
};
/*
 * This is set up by the setup-routine at boot-time
 */


#undef memcmp
#undef memset
#undef memcpy
#define memzero(s, n)     memset ((s), 0, (n))


/* FIXME handle systems with large EBDA's */
static struct parameters *faked_real_mode = (void *)REAL_MODE_DATA_LOC;



/*
 * Output
 * =============================================================================
 */

/* Base Address */
#define TTYS0 0x3f8
#define TTYS0_LSR (TTYS0+0x05)
#define TTYS0_TBR (TTYS0+0x00)

static void ttys0_tx_byte(unsigned byte)
{
	/* Wait until I can send a byte */
	while((inb(TTYS0_LSR) & 0x20) == 0)
		;
	outb(byte, TTYS0_TBR);
	/* Wait until the byte is transmitted */
	while(!(inb(TTYS0_LSR) & 0x40))
		;
}
static void put_char_serial(int c)
{
	if (c == '\n') {
		ttys0_tx_byte('\r');
	}
	ttys0_tx_byte(c);
}

static void putchar(int c)
{
#if 0
	put_char_video(c);
#endif
	put_char_serial(c);
}

#define LONG_LONG_SHIFT  ((int)((sizeof(unsigned long long)*CHAR_BIT) - 4))
#define LONG_SHIFT  ((int)((sizeof(unsigned long)*CHAR_BIT) - 4))
#define INT_SHIFT   ((int)((sizeof(unsigned int)*CHAR_BIT) - 4))
#define SHRT_SHIFT  ((int)((sizeof(unsigned short)*CHAR_BIT) - 4))
#define CHAR_SHIFT  ((int)((sizeof(unsigned char)*CHAR_BIT) - 4))

/**************************************************************************
PRINTF and friends

	Formats:
		%x	- 4 bytes int (8 hex digits, lower case)
		%X	- 4 bytes int (8 hex digits, upper case)
		%lx     - 8 bytes long (16 hex digits, lower case)
		%lX     - 8 bytes long (16 hex digits, upper case)
		%hx	- 2 bytes int (4 hex digits, lower case)
		%hX	- 2 bytes int (4 hex digits, upper case)
		%hhx	- 1 byte int (2 hex digits, lower case)
		%hhX	- 1 byte int (2 hex digits, upper case)
			- optional # prefixes 0x or 0X
		%d	- decimal int
		%c	- char
		%s	- string
	Note: width specification not supported
**************************************************************************/
static void printf(const char *fmt, ...)
{
	va_list args;
	char *p;
	va_start(args, fmt);
	for ( ; *fmt != '\0'; ++fmt) {
		if (*fmt != '%') {
			putchar(*fmt);
			continue;
		}
		if (*++fmt == 's') {
			for(p = va_arg(args, char *); *p != '\0'; p++)
				putchar(*p);
		}
		else {	/* Length of item is bounded */
			char tmp[40], *q = tmp;
			int shift = INT_SHIFT;
			if (*fmt == 'L') {
				shift = LONG_LONG_SHIFT;
				fmt++;
			}
			else if (*fmt == 'l') {
				shift = LONG_SHIFT;
				fmt++;
			}
			else if (*fmt == 'h') {
				shift = SHRT_SHIFT;
				fmt++;
				if (*fmt == 'h') {
					shift = CHAR_SHIFT;
					fmt++;
				}
			}

			/*
			 * Before each format q points to tmp buffer
			 * After each format q points past end of item
			 */
			if ((*fmt | 0x20) == 'x') {
				/* With x86 gcc, sizeof(long) == sizeof(int) */
				unsigned long long h;
				int ncase;
				if (shift > LONG_SHIFT) {
					h = va_arg(args, unsigned long long);
				}
				else if (shift > INT_SHIFT) {
					h = va_arg(args, unsigned long);
				} else {
					h = va_arg(args, unsigned int);
				}
				ncase = (*fmt & 0x20);
				for ( ; shift >= 0; shift -= 4)
					*q++ = "0123456789ABCDEF"[(h >> shift) & 0xF] | ncase;
			}
			else if (*fmt == 'd') {
				char *r;
				long i;
				if (shift > LONG_SHIFT) {
					i = va_arg(args, long long);
				}
				else if (shift > INT_SHIFT) {
					i = va_arg(args, long);
				} else {
					i = va_arg(args, int);
				}
				if (i < 0) {
					*q++ = '-';
					i = -i;
				}
				p = q;		/* save beginning of digits */
				do {
					*q++ = '0' + (i % 10);
					i /= 10;
				} while (i);
				/* reverse digits, stop in middle */
				r = q;		/* don't alter q */
				while (--r > p) {
					i = *r;
					*r = *p;
					*p++ = i;
				}
			}
			else if (*fmt == 'c')
				*q++ = va_arg(args, int);
			else
				*q++ = *fmt;
			/* now output the saved string */
			for (p = tmp; p < q; ++p)
				putchar(*p);
		}
	}
	va_end(args);
}

/*
 * String Functions
 * =============================================================================
 */


size_t strnlen(const char *s, size_t max)
{
	size_t len = 0;
	while(len < max && *s) {
		len++;
		s++;
	}
	return len;
}

void* memset(void* s, int c, size_t n)
{
	size_t i;
	char *ss = (char*)s;

	for (i=0;i<n;i++) ss[i] = c;
	return s;
}

void* memcpy(void *dest, const void *src, size_t len)
{
	size_t i;
	unsigned char *d;
	const unsigned char *s;
	d = dest;
	s = src;

	for (i=0; i < len; i++)
		d[i] = s[i];

	return dest;
}

int memcmp(void *src1, void *src2, size_t len)
{
	unsigned char *s1, *s2;
	size_t i;
	s1 = src1;
	s2 = src2;
	for(i = 0; i < len; i++) {
		if (*s1 != *s2) {
			return *s2 - *s1;
		}
	}
	return 0;

}

/*
 * Checksum functions
 * =============================================================================
 */


static unsigned long checksum_partial(unsigned long sum,
	void *addr, unsigned long length)
{
	uint8_t *ptr;
	volatile union {
		uint8_t  byte[2];
		uint16_t word;
	} value;
	unsigned long i;
	/* In the most straight forward way possible,
	 * compute an ip style checksum.
	 */
	sum = 0;
	ptr = addr;
	for(i = 0; i < length; i++) {
		unsigned long value;
		value = ptr[i];
		if (i & 1) {
			value <<= 8;
		}
		/* Add the new value */
		sum += value;
		/* Wrap around the carry */
		if (sum > 0xFFFF) {
			sum = (sum + (sum >> 16)) & 0xFFFF;
		}
	}
	value.byte[0] = sum & 0xff;
	value.byte[1] = (sum >> 8) & 0xff;
	return value.word & 0xFFFF;

}

static unsigned long checksum_final(unsigned long partial)
{
	return (~partial) & 0xFFFF;
}

static unsigned long compute_checksum(void *vaddr, unsigned long length)
{
	return checksum_final(checksum_partial(0, vaddr, length));
}

/*
 * Helper functions
 * =============================================================================
 */


void append_command_line(struct parameters *real_mode, char *arg, int arg_bytes)
{
	int len, max;
	char *dest;
	/* skip over what has already been set */
	len = strnlen(real_mode->command_line, sizeof(real_mode->command_line));
	dest = real_mode->command_line + len;
	max = sizeof(real_mode->command_line) - 1;
	if (max < 1) {
		/* No room to append anything :( */
		return;
	}
	/* Add a space in between strings */
	*dest++ = ' ';
	/* Append the added command line */
	max = sizeof(real_mode->command_line) - 1;
	if (max > arg_bytes) {
		max = arg_bytes;
	}
	len = strnlen(arg, max);
	memcpy(dest, arg, len);
	dest += len;
	/* Null terminate the string */
	*dest++ = '\0';
}

static void set_memsize_k(struct parameters *real_mode, unsigned long mem_k)
{
	/* ALT_MEM_K maxes out at 4GB */
	if (mem_k > 0x3fffff) {
		mem_k = 0x3fffff;
	}
	if (mem_k > (real_mode->alt_mem_k + (1 << 10))) {
		/* Use our memory size less 1M */
		real_mode->alt_mem_k = mem_k - (1 << 10);
		real_mode->ext_mem_k = mem_k - (1 << 10);
		if ((mem_k - (1 << 10)) > 0xFFFF) {
			real_mode->ext_mem_k = 0xFC00; /* 64 M */
		}
	}
}

static void add_e820_map(struct parameters *real_mode,
	unsigned long long addr, unsigned long long size,
	unsigned long type)
{
	unsigned long long high;
	unsigned long mem_k;
	int i;
	i = real_mode->e820_map_nr;
	if (i < E820MAX) {
		real_mode->e820_map[i].addr = addr;
		real_mode->e820_map[i].size = size;
		real_mode->e820_map[i].type = type;
		real_mode->e820_map_nr++;
	}
	/* policy I assume that for the legacy memory
	 * variables memory is contiguous.
	 */
	if (type == E820_RAM) {
		high = addr + size;
		if (high >= 0x40000000000ULL) {
			mem_k = 0xFFFFFFFF;
		} else {
			mem_k = high >> 10;
		}
		set_memsize_k(real_mode, mem_k);
	}
}

/*
 * Multiboot
 * =============================================================================
 */

#define MULTI_MEM_DEBUG 0
#if MULTI_MEM_DEBUG
#define multi_puts(x) printf("%s", x)
#define multi_put_hex(x) printf("%x", x)
#define multi_put_lhex(x) printf("%Lx", x)
#else
#define multi_puts(x)
#define multi_put_hex(x)
#define multi_put_lhex(x)
#endif /* MULTI_MEM_DEBUG */

/* Multiboot Specification */
struct multiboot_mods {
	unsigned mod_start;
	unsigned mod_end;
	unsigned char *string;
	unsigned reserved;
};

struct memory_segment {
	unsigned long long addr;
	unsigned long long size;
	unsigned type;
};

struct multiboot_info {
        unsigned flags;
#define MULTIBOOT_MEM_VALID       0x01
#define MULTIBOOT_BOOT_DEV_VALID  0x02
#define MULTIBOOT_CMDLINE_VALID   0x04
#define MULTIBOOT_MODS_VALID      0x08
#define MULTIBOOT_AOUT_SYMS_VALID 0x10
#define MULTIBOOT_ELF_SYMS_VALID  0x20
#define MULTIBOOT_MMAP_VALID      0x40
	unsigned mem_lower;
	unsigned mem_upper;
	unsigned char boot_device[4];
	void *command_line;
	unsigned mods_count;
	struct multiboot_mods *mods_addr;
	unsigned syms_num;
	unsigned syms_size;
	unsigned syms_addr;
	unsigned syms_shndx;
	unsigned mmap_length;
	struct memory_segment *mmap_addr;
};

#define MULTIBOOT_MAX_COMMAND_LINE 0xFFFFFFFF

static void convert_multiboot_memmap(
	struct parameters *real_mode,
	struct multiboot_info *info)
{
	unsigned size;
	unsigned *size_addr;
#define next_seg(seg, size) ((struct memory_segment *)((char *)(seg) + (size)))
	struct memory_segment *seg, *end;

	seg = info->mmap_addr;
	end  = (void *)(((char *)seg) + info->mmap_length);
	size_addr = (unsigned *)(((char *)seg) - 4);
	size = *size_addr;
	multi_puts("mmap_addr: "); multi_put_hex((unsigned)info->mmap_addr); multi_puts("\n");
	multi_puts("mmap_length: "); multi_put_hex(info->mmap_length); multi_puts("\n");
	multi_puts("size_addr: "); multi_put_hex((unsigned)size_addr); multi_puts("\n");
	multi_puts("size: "); multi_put_hex(size); multi_puts("\n");
	multi_puts("end: "); multi_put_hex((unsigned)end); multi_puts("\n");
	for(seg = info->mmap_addr; (seg < end); seg = next_seg(seg,size)) {
		multi_puts("multi-mem: ");
		multi_put_lhex(seg->size);
		multi_puts(" @ ");
		multi_put_lhex(seg->addr);
		multi_puts(" (");
		switch(seg->type) {
		case E820_RAM:
			multi_puts("ram");
			break;
		case E820_ACPI:
			multi_puts("ACPI data");
			break;
		case E820_NVS:
			multi_puts("ACPI NVS");
			break;
		case E820_RESERVED:
		default:
			multi_puts("reserved");
			break;
		}
		multi_puts(")\n");
		add_e820_map(real_mode, seg->addr, seg->size, seg->type);
	}
#undef next_seg
}

static void convert_multiboot(
	struct param_info *info, struct multiboot_info *mb_info)
{
	if (info->need_mem_sizes && (mb_info->flags & MULTIBOOT_MEM_VALID)) {
		/* info->memory is short 1M */
		set_memsize_k(info->real_mode, mb_info->mem_upper + (1 << 10));
	}
	if (mb_info->flags & MULTIBOOT_CMDLINE_VALID) {
		append_command_line(info->real_mode, mb_info->command_line,
			MULTIBOOT_MAX_COMMAND_LINE);
	}
	if (info->need_mem_sizes && (mb_info->flags & MULTIBOOT_MMAP_VALID)) {
		convert_multiboot_memmap(info->real_mode, mb_info);
	}
	if (mb_info->flags & (MULTIBOOT_MEM_VALID | MULTIBOOT_MMAP_VALID)) {
		info->need_mem_sizes = 0;
	}
}


/*
 * Uniform Boot Environment
 * =============================================================================
 */

#define UBE_MEM_DEBUG 0
#if UBE_MEM_DEBUG
#define ube_puts(x) printf("%s", x)
#define ube_put_hex(x) printf("%x", x)
#define ube_put_lhex(x) printf("%Lx", x)
#else
#define ube_puts(x)
#define ube_put_hex(x)
#define ube_put_lhex(x)
#endif /* UBE_MEM_DEBUG */
static void convert_uniform_boot_memory(
	struct parameters *real_mode, struct ube_memory *mem)
{
	int i;
	int entries;
	unsigned long mem_k;
	mem_k = 0;
	entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]);
	for(i = 0; (i < entries) && (i < E820MAX); i++) {
		unsigned long type;
		ube_puts("ube-mem: ");
		ube_put_lhex(mem->map[i].size);
		ube_puts(" @ ");
		ube_put_lhex(mem->map[i].start);
		ube_puts(" (");
		switch(mem->map[i].type) {
		case UBE_MEM_RAM:
			type = E820_RAM;
			ube_puts("ram");
			break;
		case UBE_MEM_ACPI:
			type = E820_ACPI;
			ube_puts("ACPI data");
			break;
		case UBE_MEM_NVS:
			type = E820_NVS;
			ube_puts("ACPI NVS");
			break;
		case UBE_MEM_RESERVED:
		default:
			type = E820_RESERVED;
			ube_puts("reserved");
			break;
		}
		ube_puts(")\n");
		add_e820_map(real_mode,
			mem->map[i].start, mem->map[i].size, type);
	}
}

static void convert_uniform_boot(struct param_info *info,
	struct uniform_boot_header *header)
{
	/* Uniform boot environment */
	unsigned long env_bytes;
	char *env;
	if (header->arg_bytes) {
		append_command_line(info->real_mode, (void *)(header->arg), header->arg_bytes);
	}
	env = (void *)(header->env);
	env_bytes = header->env_bytes;
	while(env_bytes) {
		struct ube_record *record;
		record = (void *)env;
		if (record->tag == UBE_TAG_MEMORY) {
			if (info->need_mem_sizes) {
				convert_uniform_boot_memory(info->real_mode, (void *)record);
				info->need_mem_sizes = 0;
			}
		}
		env += record->size;
		env_bytes -= record->size;
	}
}




/*
 * Hardware
 * =============================================================================
 */

/* we're getting screwed again and again by this problem of the 8259.
 * so we're going to leave this lying around for inclusion into
 * crt0.S on an as-needed basis.
 * well, that went ok, I hope. Now we have to reprogram the interrupts :-(
 * we put them right after the intel-reserved hardware interrupts, at
 * int 0x20-0x2F. There they won't mess up anything. Sadly IBM really
 * messed this up with the original PC, and they haven't been able to
 * rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,
 * which is used for the internal hardware interrupts as well. We just
 * have to reprogram the 8259's, and it isn't fun.
 */

static void setup_i8259(void)
{
	outb(0x11, 0x20);		/*! initialization sequence to 8259A-1*/
	outb(0x11, 0xA0);		/*! and to 8259A-2*/

	outb(0x20, 0x21);		/*! start of hardware int's (0x20)*/
	outb(0x28, 0xA1);		/*! start of hardware int's 2 (0x28)*/

	outb(0x04, 0x21);		/*! 8259-1 is master*/
	outb(0x02, 0xA1);		/*! 8259-2 is slave*/

	outb(0x01, 0x21);		/*! 8086 mode for both*/
	outb(0x01, 0xA1);

	outb(0xFF, 0xA1);		/*! mask off all interrupts for now*/
	outb(0xFB, 0x21);		/*! mask all irq's but irq2 which is cascaded*/
}

static void hardware_setup(struct param_info *info __unused)
{
	/* Disable nmi */
	outb(0x80, 0x70);

	/* Make sure any coprocessor is properly reset.. */
	outb(0, 0xf0);
	outb(0, 0xf1);

	setup_i8259();
}


/*
 * ELF Boot loader
 * =============================================================================
 */

static int count_elf_notes(Elf_Bhdr *bhdr)
{
	unsigned char *note, *end;
	int count;
	count = 0;
	note = ((char *)bhdr) + sizeof(*bhdr);
	end = ((char *)bhdr) + bhdr->b_size;
#if 0
	printf("count_elf_notes %lx\n", (unsigned long)bhdr);
#endif
	while (note < end) {
		Elf_Nhdr *hdr;
		unsigned char *n_name, *n_desc, *next;
		hdr = (Elf_Nhdr *)note;
		n_name = note + sizeof(*hdr);
		n_desc = n_name + ((hdr->n_namesz + 3) & ~3);
		next = n_desc + ((hdr->n_descsz + 3) & ~3);
#if 0
		printf("elf_note = %lx\n", (unsigned long)note);
		printf("elf_namesz = %x\n", hdr->n_namesz);
		printf("elf_descsz = %x\n", hdr->n_descsz);
		printf("elf_type   = %x\n", hdr->n_type);
		printf("elf_name = %lx\n", (unsigned long)n_name);
		printf("elf_desc = %lx\n", (unsigned long)n_desc);
#endif
		if (next > end)
			break;
		count++;
		note = next;
	}
	return count;
}

static Elf_Nhdr *find_elf_note(Elf_Bhdr *bhdr,
	Elf_Word namesz, unsigned char *name, Elf_Word type)
{
	unsigned char *note, *end;
	note = ((char *)bhdr) + sizeof(*bhdr);
	end = ((char *)bhdr) + bhdr->b_size;
	while(note < end) {
		Elf_Nhdr *hdr;
		unsigned char *n_name, *n_desc, *next;
		hdr = (Elf_Nhdr *)note;
		n_name = note + sizeof(*hdr);
		n_desc = n_name + ((hdr->n_namesz + 3) & ~3);
		next = n_desc + ((hdr->n_descsz + 3) & ~3);
		if (next > end)
			break;
		if ((hdr->n_type == type) &&
			(hdr->n_namesz == namesz) &&
			(memcmp(n_name, name, namesz) == 0)) {
			return hdr;
		}
		note = next;
	}
	return 0;
}

static void convert_elf_command_line(struct param_info *info,
	Elf_Word descsz, unsigned char *desc)
{
	append_command_line(info->real_mode, desc, descsz);
}

struct {
	Elf_Word namesz;
	unsigned char *name;
	Elf_Word type;
	void (*convert)(struct param_info *info, Elf_Word descsz, unsigned char *desc);
} elf_notes[] =
{
	{ 0, "", EBN_COMMAND_LINE, convert_elf_command_line },
};

static void convert_elf_boot(struct param_info *info, Elf_Bhdr *bhdr)
{
	unsigned char *note, *end;
	note = ((char *)bhdr) + sizeof(*bhdr);
	end = ((char *)bhdr) + bhdr->b_size;
	while(note < end) {
		Elf_Nhdr *hdr;
		unsigned char *n_name, *n_desc, *next;
		size_t i;
		hdr = (Elf_Nhdr *)note;
		n_name = note + sizeof(*hdr);
		n_desc = n_name + ((hdr->n_namesz + 3) & ~3);
		next = n_desc + ((hdr->n_descsz + 3) & ~3);
		if (next > end)
			break;
		for(i = 0; i < sizeof(elf_notes)/sizeof(elf_notes[0]); i++) {
			if ((hdr->n_type == elf_notes[i].type) &&
				(hdr->n_namesz == elf_notes[i].namesz) &&
				(memcmp(n_name, elf_notes[i].name, elf_notes[i].namesz) == 0)) {
				elf_notes[i].convert(info, hdr->n_descsz, n_desc);
				break;
			}
		}
		note = next;
	}
}

/*
 * LinuxBIOS
 * =============================================================================
 */

#define LB_MEM_DEBUG 0
#if LB_MEM_DEBUG
#define lb_puts(x) printf("%s", x)
#define lb_put_hex(x) printf("%x", x)
#define lb_put_lhex(x) printf("%Lx", x)
#else
#define lb_puts(x)
#define lb_put_hex(x)
#define lb_put_lhex(x)
#endif /* LB_MEM_DEBUG */

static unsigned count_lb_records(void *start, unsigned long length)
{
	struct lb_record *rec;
	void *end;
	unsigned count;
	count = 0;
	end = ((char *)start) + length;
	for(rec = start; ((void *)rec < end) &&
		(rec->size <= (unsigned long)(end - (void *)rec));
		rec = (void *)(((char *)rec) + rec->size)) {
		count++;
	}
	return count;
}

static struct lb_header *__find_lb_table(void *start, void *end)
{
	unsigned char *ptr;
	/* For now be stupid.... */
	for(ptr = start; (void *)ptr < end; ptr += 16) {
		struct lb_header *head = (void *)ptr;
		if ((head->signature[0] == 'L') &&
			(head->signature[1] == 'B') &&
			(head->signature[2] == 'I') &&
			(head->signature[3] == 'O') &&
			(head->header_bytes == sizeof(*head)) &&
			(compute_checksum(head, sizeof(*head)) == 0) &&
			(compute_checksum(ptr + sizeof(*head), head->table_bytes) ==
				head->table_checksum) &&
			(count_lb_records(ptr + sizeof(*head), head->table_bytes) ==
				head->table_entries)
			) {
			return head;
		}
	};
	return 0;
}

static int find_lb_table(struct param_info *info)
{
	struct lb_header *head;
	head = 0;
	if (!head) {
		/* First try at address 0 */
		head = __find_lb_table((void *)0x00000, (void *)0x1000);
	}
	if (!head) {
		/* Then try at address 0xf0000 */
		head = __find_lb_table((void *)0xf0000, (void *)0x100000);
	}
	if (head) {
		struct lb_forward *forward = (struct lb_forward *)(((char *)head) + head->header_bytes);
		if (forward->tag == LB_TAG_FORWARD) {
			head = __find_lb_table(forward->forward,
					forward->forward + 0x1000);
		}
	}
	if (head) {
		info->has_linuxbios = 1;
		info->lb_table = head;
		return 1;
	}
	return 0;
}

static void convert_lb_memory(struct param_info *info, struct lb_memory *mem)
{
	int i;
	int entries;
	entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]);
	for(i = 0; (i < entries) && (i < E820MAX); i++) {
		unsigned long type;
		unsigned long long end;
		end = mem->map[i].start + mem->map[i].size;
		lb_puts("lb-mem: ");
		lb_put_lhex(mem->map[i].start);
		lb_puts(" - ");
		lb_put_lhex(end);
		lb_puts(" (");
		switch(mem->map[i].type) {
		case LB_MEM_RAM:
			type = E820_RAM;
			lb_puts("ram");
			break;
		default:
			type = E820_RESERVED;
			lb_puts("reserved");
			break;
		}
		lb_puts(")\n");
		add_e820_map(info->real_mode,
			mem->map[i].start, mem->map[i].size, type);
	}
	info->need_mem_sizes = 0;
}

static void query_lb_values(struct param_info *info)
{
	struct lb_header *head;
	struct lb_record *rec;
	void *start, *end;
	head = info->lb_table;
	start = ((unsigned char *)head) + sizeof(*head);
	end = ((char *)start) + head->table_bytes;
	for(rec = start; ((void *)rec < end) &&
		(rec->size <= (unsigned long)(end - (void *)rec));
		rec = (void *)(((char *)rec) + rec->size)) {
		switch(rec->tag) {
		case LB_TAG_MEMORY:
		{
			struct lb_memory *mem;
			mem = (struct lb_memory *) rec;
			convert_lb_memory(info, mem);
			break;
		}
		default:
			break;
		};
	}
}

/*
 * PCBIOS
 * =============================================================================
 */
#define PC_MEM_DEBUG 0
#if PC_MEM_DEBUG
#define pc_puts(x) printf("%s", x)
#define pc_put_hex(x) printf("%x", x)
#define pc_put_lhex(x) printf("%Lx", x)
#else
#define pc_puts(x)
#define pc_put_hex(x)
#define pc_put_lhex(x)
#endif /* PC_MEM_DEBUG */

/* functions for querying the pcbios */
extern void noop(void); /* for testing purposes only */
extern int meme820(struct e820entry *buf, int count);
extern unsigned int meme801(void);
extern unsigned short mem88(void);
extern unsigned short basememsize(void);

struct meminfo {
	int map_count;
	struct e820entry map[E820MAX];
};

static struct meminfo meminfo;
static void get_meminfo(struct param_info *info)
{
	int i;
	pc_puts("getting meminfo...\n");
	meminfo.map_count = meme820(meminfo.map, E820MAX);
	pc_puts("got meminfo count="); pc_put_hex(meminfo.map_count); pc_puts("\n");
	for(i = 0; i < meminfo.map_count; i++) {
		unsigned long long end;
		struct e820entry *seg = meminfo.map + i;
		end = seg->addr + seg->size;
		pc_puts("BIOS-e820: ");
		pc_put_lhex(seg->addr);
		pc_puts(" - ");
		pc_put_lhex(end);
		pc_puts(" (");
		switch(seg->type) {
		case E820_RAM:
			pc_puts("ram");
			info->need_mem_sizes = 0;
			break;
		case E820_ACPI:
			pc_puts("ACPI data");
			break;
		case E820_NVS:
			pc_puts("ACPI NVS");
			break;
		case E820_RESERVED:
		default:
			pc_puts("reserved");
			break;
		}
		pc_puts(")\n");
		add_e820_map(info->real_mode,
			seg->addr, seg->size, seg->type);
	}
	info->real_mode->alt_mem_k = meme801();
	info->real_mode->ext_mem_k = mem88();
	if (info->real_mode->alt_mem_k || info->real_mode->ext_mem_k) {
		info->need_mem_sizes = 0;
	}
}

static void query_pcbios_values(struct param_info *info)
{
	get_meminfo(info);
}

/*
 * Bootloaders
 * =============================================================================
 */


static void query_bootloader_param_class(struct param_info *info)
{
	int has_bootloader_type = 0;
	Elf_Bhdr *hdr = 0;
	if (!has_bootloader_type && (info->type == 0x2BADB002)) {
		/* Orignal multiboot specification */
		info->has_multiboot = 1;
		has_bootloader_type = 1;
	}
	if (!has_bootloader_type && (info->type == 0x0A11B007)) {
		/* Uniform boot proposal */
		unsigned long checksum;
		struct uniform_boot_header *header;
		header = info->data;
		checksum = compute_checksum(header, header->header_bytes);
		if (checksum == 0) {
			info->has_uniform_boot = 1;
			has_bootloader_type = 1;
		} else{
			printf("Bad uniform boot header checksum!\n");
		}
	}
	if (info->type == ELF_BHDR_MAGIC) {
		hdr = info->data;
	}
	if (info->param && (info->param->b_signature == ELF_BHDR_MAGIC)) {
		hdr = info->param;
	}
	if (!has_bootloader_type && hdr) {
		/* Good ELF boot proposal... */
		unsigned long checksum;
		int count;
		checksum = 0;
		if (hdr->b_checksum != 0) {
			checksum = compute_checksum(hdr, hdr->b_size);
		}
		count = count_elf_notes(hdr);
		if ((hdr->b_signature == ELF_BHDR_MAGIC) &&
			(checksum == 0) &&
			hdr->b_records == count) {
			info->has_elf_boot = 1;
			info->param = hdr;
			has_bootloader_type = 1;
		}
		else {
			printf("Bad ELF parameter table!\n");
			printf("   checksum = %x\n", checksum);
			printf("      count = %x\n", count);
			printf("        hdr = %x\n", (unsigned long)hdr);
			printf("     b_size = %x\n", hdr->b_size);
			printf("b_signature = %x\n", hdr->b_signature);
			printf("  b_records = %x\n", hdr->b_records);
		}
	}
	if (!has_bootloader_type) {
		printf("Unknown bootloader class!\n");
		printf("type=%x\n", info->type);
		printf("data=%x\n", (unsigned)info->data);
		printf("param=%x\n", (unsigned)info->param);
	}
}

static void query_bootloader_values(struct param_info *info)
{
	if (info->has_multiboot) {
		convert_multiboot(info, info->data);
	}
	else if (info->has_uniform_boot) {
		convert_uniform_boot(info, info->data);
	}
	else if (info->has_elf_boot) {
		convert_elf_boot(info, info->param);
	}
}

/*
 * Firmware
 * =============================================================================
 */

static int bootloader_query_firmware_class(struct param_info *info)
{
	Elf_Nhdr *hdr;
	unsigned char *note, *n_name, *n_desc;
	int detected_firmware_type;
	if (!info->has_elf_boot) {
		/* Only the elf boot tables gives us a firmware type */
		return 0;
	}
	detected_firmware_type = 0;
	n_desc = 0;

	hdr = find_elf_note(info->param, 0, 0, EBN_FIRMWARE_TYPE);
	if (!hdr) {
		/* If I'm not explicitly told the firmware type
		 * do my best to guess it for myself.
		 */
		detected_firmware_type = 0;
	} else {
		note = (char *)hdr;
		n_name = note + sizeof(*hdr);
		n_desc = n_name + ((hdr->n_namesz + 3) & ~3);
	}
	if (!detected_firmware_type && hdr &&
		(hdr->n_descsz == 7) &&
		(memcmp(n_desc, "PCBIOS", 7) == 0)) {
		info->has_pcbios = 1;
		detected_firmware_type = 1;
	}
	if (!detected_firmware_type && hdr &&
		(hdr->n_descsz == 10) &&
		(memcmp(n_desc, "LinuxBIOS", 10) == 0)) {
		/* Don't believe I'm linuxBIOS unless I can
		 * find the linuxBIOS table..
		 */
		detected_firmware_type = find_lb_table(info);
	}
	if (!detected_firmware_type && hdr &&
		(hdr->n_descsz == 0)) {
		/* No firmware is present */
		detected_firmware_type = 1;
	}
	if (!detected_firmware_type && hdr &&
		(hdr->n_descsz == 1) &&
		(memcmp(n_desc, "", 1) == 0)) {
		/* No firmware is present */
		detected_firmware_type = 1;
	}
	if (!detected_firmware_type && hdr) {
		printf("Unknown firmware type: %s\n", n_desc);
	}
	return detected_firmware_type;
}

static void query_firmware_class(struct param_info *info)
{
	int detected_firmware_type = 0;

	/* First say I have no firmware at all... */
	info->has_pcbios = 0;
	info->has_linuxbios = 0;

	/* See if the bootloader has told us what
	 * kind of firmware we are running on.
	 */
	detected_firmware_type = bootloader_query_firmware_class(info);

	/* See if we can detect linuxbios. */
	if (!detected_firmware_type) {
		detected_firmware_type = find_lb_table(info);
	}

	if (!detected_firmware_type) {
		/* if all else fails assume a standard pcbios... */
		info->has_pcbios = 1;
	}

	/* Now print out the firmware type... */
	printf("Firmware type:");
	if (info->has_linuxbios) {
		printf(" LinuxBIOS");
	}
	if (info->has_pcbios) {
		printf(" PCBIOS");
	}
	printf("\n");
}

static void query_firmware_values(struct param_info *info)
{
	if (info->has_linuxbios) {
		query_lb_values(info);
	}
	if (info->has_pcbios) {
		query_pcbios_values(info);
	}

}

/*
 * Debug
 * =============================================================================
 */

#if 0
static void print_offsets(void)
{
	struct parameters *real_mode = 0;
	printf("print_offsets\n");

	printf("orig_x               =%x\n", (uint32_t)&real_mode->orig_x);
	printf("orig_y               =%x\n", (uint32_t)&real_mode->orig_y);
	printf("ext_mem_k            =%x\n", (uint32_t)&real_mode->ext_mem_k);
	printf("orig_video_page      =%x\n", (uint32_t)&real_mode->orig_video_page);
	printf("orig_video_mode      =%x\n", (uint32_t)&real_mode->orig_video_mode);
	printf("orig_video_cols      =%x\n", (uint32_t)&real_mode->orig_video_cols);
	printf("unused2              =%x\n", (uint32_t)&real_mode->unused2);
	printf("orig_video_ega_bx    =%x\n", (uint32_t)&real_mode->orig_video_ega_bx);
	printf("unused3              =%x\n", (uint32_t)&real_mode->unused3);
	printf("orig_video_lines     =%x\n", (uint32_t)&real_mode->orig_video_lines);
	printf("orig_video_isVGA     =%x\n", (uint32_t)&real_mode->orig_video_isVGA);
	printf("orig_video_points    =%x\n", (uint32_t)&real_mode->orig_video_points);
	printf("lfb_width            =%x\n", (uint32_t)&real_mode->lfb_width);
	printf("lfb_height           =%x\n", (uint32_t)&real_mode->lfb_height);
	printf("lfb_depth            =%x\n", (uint32_t)&real_mode->lfb_depth);
	printf("lfb_base             =%x\n", (uint32_t)&real_mode->lfb_base);
	printf("lfb_size             =%x\n", (uint32_t)&real_mode->lfb_size);
	printf("cl_magic             =%x\n", (uint32_t)&real_mode->cl_magic);
	printf("cl_offset            =%x\n", (uint32_t)&real_mode->cl_offset);
	printf("lfb_linelength       =%x\n", (uint32_t)&real_mode->lfb_linelength);
	printf("red_size             =%x\n", (uint32_t)&real_mode->red_size);
	printf("red_pos              =%x\n", (uint32_t)&real_mode->red_pos);
	printf("green_size           =%x\n", (uint32_t)&real_mode->green_size);
	printf("green_pos            =%x\n", (uint32_t)&real_mode->green_pos);
	printf("blue_size            =%x\n", (uint32_t)&real_mode->blue_size);
	printf("blue_pos             =%x\n", (uint32_t)&real_mode->blue_pos);
	printf("rsvd_size            =%x\n", (uint32_t)&real_mode->rsvd_size);
	printf("rsvd_pos             =%x\n", (uint32_t)&real_mode->rsvd_pos);
	printf("vesapm_seg           =%x\n", (uint32_t)&real_mode->vesapm_seg);
	printf("vesapm_off           =%x\n", (uint32_t)&real_mode->vesapm_off);
	printf("pages                =%x\n", (uint32_t)&real_mode->pages);
	printf("reserved4            =%x\n", (uint32_t)&real_mode->reserved4);
	printf("apm_bios_info        =%x\n", (uint32_t)&real_mode->apm_bios_info);
	printf("drive_info           =%x\n", (uint32_t)&real_mode->drive_info);
	printf("sys_desc_table       =%x\n", (uint32_t)&real_mode->sys_desc_table);
	printf("alt_mem_k            =%x\n", (uint32_t)&real_mode->alt_mem_k);
	printf("reserved5            =%x\n", (uint32_t)&real_mode->reserved5);
	printf("e820_map_nr          =%x\n", (uint32_t)&real_mode->e820_map_nr);
	printf("reserved6            =%x\n", (uint32_t)&real_mode->reserved6);
	printf("mount_root_rdonly    =%x\n", (uint32_t)&real_mode->mount_root_rdonly);
	printf("reserved7            =%x\n", (uint32_t)&real_mode->reserved7);
	printf("ramdisk_flags        =%x\n", (uint32_t)&real_mode->ramdisk_flags);
	printf("reserved8            =%x\n", (uint32_t)&real_mode->reserved8);
	printf("orig_root_dev        =%x\n", (uint32_t)&real_mode->orig_root_dev);
	printf("reserved9            =%x\n", (uint32_t)&real_mode->reserved9);
	printf("aux_device_info      =%x\n", (uint32_t)&real_mode->aux_device_info);
	printf("reserved10           =%x\n", (uint32_t)&real_mode->reserved10);
	printf("param_block_signature=%x\n", (uint32_t)&real_mode->param_block_signature);
	printf("param_block_version  =%x\n", (uint32_t)&real_mode->param_block_version);
	printf("reserved11           =%x\n", (uint32_t)&real_mode->reserved11);
	printf("loader_type          =%x\n", (uint32_t)&real_mode->loader_type);
	printf("loader_flags         =%x\n", (uint32_t)&real_mode->loader_flags);
	printf("reserved12           =%x\n", (uint32_t)&real_mode->reserved12);
	printf("kernel_start         =%x\n", (uint32_t)&real_mode->kernel_start);
	printf("initrd_start         =%x\n", (uint32_t)&real_mode->initrd_start);
	printf("initrd_size          =%x\n", (uint32_t)&real_mode->initrd_size);
	printf("reserved13           =%x\n", (uint32_t)&real_mode->reserved13);
	printf("e820_map             =%x\n", (uint32_t)&real_mode->e820_map);
	printf("reserved16           =%x\n", (uint32_t)&real_mode->reserved16);
	printf("command_line         =%x\n", (uint32_t)&real_mode->command_line);
	printf("reserved17           =%x\n", (uint32_t)&real_mode->reserved17);
}

static void print_linux_params(struct param_info *info)
{
	int i;

	printf("print_linux_params\n");
	/* Default screen size */
	printf("orig_x           =%x\n", info->real_mode->orig_x);
	printf("orig_y           =%x\n", info->real_mode->orig_y);
	printf("orig_video_page  =%x\n", info->real_mode->orig_video_page);
	printf("orig_video_mode  =%x\n", info->real_mode->orig_video_mode);
	printf("orig_video_cols  =%x\n", info->real_mode->orig_video_cols);
	printf("orig_video_lines =%x\n", info->real_mode->orig_video_lines);
	printf("orig_video_ega_bx=%x\n", info->real_mode->orig_video_ega_bx);
	printf("orig_video_isVGA =%x\n", info->real_mode->orig_video_isVGA);
	printf("orig_video_points=%x\n", info->real_mode->orig_video_points);


	/* System descriptor table... */
	printf("sys_dest_table_len=%x\n", info->real_mode->sys_desc_table.length);

	/* Memory sizes */
	printf("ext_mem_k        =%x\n", info->real_mode->ext_mem_k);
	printf("alt_mem_k        =%x\n", info->real_mode->alt_mem_k);
	printf("e820_map_nr      =%x\n", info->real_mode->e820_map_nr);
	for(i = 0; i < E820MAX; i++) {
		printf("addr[%x]         =%Lx\n",
			i,  info->real_mode->e820_map[i].addr);
		printf("size[%x]         =%Lx\n",
			i, info->real_mode->e820_map[i].size);
		printf("type[%x]         =%Lx\n",
			i, info->real_mode->e820_map[i].type);
	}
	printf("mount_root_rdonly=%x\n", info->real_mode->mount_root_rdonly);
	printf("ramdisk_flags    =%x\n", info->real_mode->ramdisk_flags);
	printf("orig_root_dev    =%x\n", info->real_mode->orig_root_dev);
	printf("aux_device_info  =%x\n", info->real_mode->aux_device_info);
	printf("param_block_signature=%x\n", *((uint32_t *)info->real_mode->param_block_signature));
	printf("loader_type      =%x\n", info->real_mode->loader_type);
	printf("loader_flags     =%x\n", info->real_mode->loader_flags);
	printf("initrd_start     =%x\n", info->real_mode->initrd_start);
	printf("initrd_size      =%x\n", info->real_mode->initrd_size);

	/* Where I'm putting the command line */
	printf("cl_magic         =%x\n", info->real_mode->cl_magic);
	printf("cl_offset        =%x\n", info->real_mode->cl_offset);

	/* Now print the command line */
	printf("command_line     =%s\n", info->real_mode->command_line);
}


#endif

/*
 * main
 * =============================================================================
 */

void initialize_linux_params(struct param_info *info)
{
	int len;
	/* First the defaults */
	memset(info->real_mode, 0, PAGE_SIZE);

	/* Default screen size */
	info->real_mode->orig_x = 0;
	info->real_mode->orig_y = 25;
	info->real_mode->orig_video_page = 0;
	info->real_mode->orig_video_mode = 0;
	info->real_mode->orig_video_cols = 80;
	info->real_mode->orig_video_lines = 25;
	info->real_mode->orig_video_ega_bx = 0;
	info->real_mode->orig_video_isVGA = 1;
	info->real_mode->orig_video_points = 16;

	/* Fill this in later */
	info->real_mode->ext_mem_k = 0;

	/* Fill in later... */
	info->real_mode->e820_map_nr = 0;

	/* Where I'm putting the command line */
	info->real_mode->cl_magic = CL_MAGIC_VALUE;
	info->real_mode->cl_offset = 2048;

	info->real_mode->cmd_line_ptr = info->real_mode->cl_offset + (unsigned long) info->real_mode;

	/* Now set the command line */
	len = strnlen(info->image->cmdline, sizeof(info->real_mode->command_line) -1);
	memcpy(info->real_mode->command_line, info->image->cmdline, len);
	info->real_mode->command_line[len] = '\0';

	/* from the bios initially */
	memset(&info->real_mode->apm_bios_info, 0, sizeof(info->real_mode->apm_bios_info));

	memset(&info->real_mode->drive_info, 0, sizeof(info->real_mode->drive_info));

	/* forget it for now... */
	info->real_mode->sys_desc_table.length = 0;

	/* Fill this in later */
	info->real_mode->alt_mem_k = 0;
	info->real_mode->ext_mem_k = 0;

	/* default yes: this can be overridden on the command line */
	info->real_mode->mount_root_rdonly = 0xFFFF;

	/* old ramdisk options, These really should be command line
	 * things...
	 */
	info->real_mode->ramdisk_flags = info->image->ramdisk_flags;

	/* default to /dev/hda.
	 * Override this on the command line if necessary
	 */
	info->real_mode->orig_root_dev = info->image->root_dev;

	/* Originally from the bios? */
	info->real_mode->aux_device_info = 0;

	/* Boot block magic */
	memcpy(info->real_mode->param_block_signature, "HdrS", 4);
	info->real_mode->param_block_version = 0x0201;

	/* Say I'm a kernel boot loader */
	info->real_mode->loader_type = (LOADER_TYPE_KERNEL << 4) + 0 /* version */;

	/* No loader flags */
	info->real_mode->loader_flags = 0;

	/* Set it to 16M, instead of 0 which means 4G */
	info->real_mode->kernel_alignment = 16*1024*1024;

	/* Ramdisk address and size ... */
	info->real_mode->initrd_start = 0;
	info->real_mode->initrd_size = 0;
	if (info->image->initrd_size) {
		info->real_mode->initrd_start = info->image->initrd_start;
		info->real_mode->initrd_size = info->image->initrd_size;
	}

	/* Now remember those things that I need */
	info->need_mem_sizes = 1;
}

void *convert_params(unsigned type, void *data, void *param, void *image)
{
	struct param_info info;
#if 0
	printf("hello world\n");
#endif
	info.real_mode = faked_real_mode;
	info.type  = type;
	info.data  = data;
	info.param = param;
	info.image = image;
	initialize_linux_params(&info);
	query_bootloader_param_class(&info);
	query_firmware_class(&info);
	query_firmware_values(&info);
	query_bootloader_values(&info);

	/* Do the hardware setup that linux might forget... */
	hardware_setup(&info);

	/* Print some debugging information */
#if 0
	printf("EXT_MEM_K=%x\n", info.real_mode->ext_mem_k);
	printf("ALT_MEM_K=%x\n", info.real_mode->alt_mem_k);
#endif
#if 0
	print_offsets();
	print_linux_params(&info);
#endif
#if 0
	printf("info.real_mode = 0x%x\n", info.real_mode );
	printf("Jumping to Linux\n");
#endif
	return info.real_mode;
}