- Initial checkin of the freebios2 tree


git-svn-id: svn://svn.coreboot.org/coreboot/trunk@784 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
diff --git a/src/boot/elfboot.c b/src/boot/elfboot.c
new file mode 100644
index 0000000..da0e909
--- /dev/null
+++ b/src/boot/elfboot.c
@@ -0,0 +1,663 @@
+#include <console/console.h>
+#include <part/fallback_boot.h>
+#include <boot/elf.h>
+#include <boot/elf_boot.h>
+#include <boot/linuxbios_tables.h>
+#include <ip_checksum.h>
+#include <stream/read_bytes.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Maximum physical address we can use for the linuxBIOS bounce buffer.
+ */
+#ifndef MAX_ADDR
+#define MAX_ADDR -1UL
+#endif
+
+extern unsigned char _ram_seg;
+extern unsigned char _eram_seg;
+
+struct segment {
+	struct segment *next;
+	struct segment *prev;
+	struct segment *phdr_next;
+	struct segment *phdr_prev;
+	unsigned long s_addr;
+	unsigned long s_memsz;
+	unsigned long s_offset;
+	unsigned long s_filesz;
+};
+
+struct verify_callback {
+	struct verify_callback *next;
+	int (*callback)(struct verify_callback *vcb, 
+		Elf_ehdr *ehdr, Elf_phdr *phdr, struct segment *head);
+	unsigned long desc_offset;
+	unsigned long desc_addr;
+};
+
+struct ip_checksum_vcb {
+	struct verify_callback data;
+	unsigned short ip_checksum;
+};
+
+int verify_ip_checksum(
+	struct verify_callback *vcb, 
+	Elf_ehdr *ehdr, Elf_phdr *phdr, struct segment *head)
+{
+	struct ip_checksum_vcb *cb;
+	struct segment *ptr;
+	unsigned long bytes;
+	unsigned long checksum;
+	unsigned char buff[2], *n_desc;
+	cb = (struct ip_checksum_vcb *)vcb;
+	/* zero the checksum so it's value won't
+	 * get in the way of verifying the checksum.
+	 */
+	n_desc = 0;
+	if (vcb->desc_addr) {
+		n_desc = (unsigned char *)(vcb->desc_addr);
+		memcpy(buff, n_desc, 2);
+		memset(n_desc, 0, 2);
+	}
+	bytes = 0;
+	checksum = compute_ip_checksum(ehdr, sizeof(*ehdr));
+	bytes += sizeof(*ehdr);
+	checksum = add_ip_checksums(bytes, checksum, 
+		compute_ip_checksum(phdr, ehdr->e_phnum*sizeof(*phdr)));
+	bytes += ehdr->e_phnum*sizeof(*phdr);
+	for(ptr = head->phdr_next; ptr != head; ptr = ptr->phdr_next) {
+		checksum = add_ip_checksums(bytes, checksum,
+			compute_ip_checksum((void *)ptr->s_addr, ptr->s_memsz));
+		bytes += ptr->s_memsz;
+	}
+	if (n_desc != 0) {
+		memcpy(n_desc, buff, 2);
+	}
+	if (checksum != cb->ip_checksum) {
+		printk_err("Image checksum: %04x != computed checksum: %04x\n",
+			cb->ip_checksum, checksum);
+	}
+	return checksum == cb->ip_checksum;
+}
+
+/* The problem:  
+ * Static executables all want to share the same addresses
+ * in memory because only a few addresses are reliably present on
+ * a machine, and implementing general relocation is hard.
+ *
+ * The solution:
+ * - Allocate a buffer twice the size of the linuxBIOS image.
+ * - Anything that would overwrite linuxBIOS copy into the lower half of
+ *   the buffer. 
+ * - After loading an ELF image copy linuxBIOS to the upper half of the
+ *   buffer.
+ * - Then jump to the loaded image.
+ * 
+ * Benefits:
+ * - Nearly arbitrary standalone executables can be loaded.
+ * - LinuxBIOS is preserved, so it can be returned to.
+ * - The implementation is still relatively simple,
+ *   and much simpler then the general case implemented in kexec.
+ * 
+ */
+
+static unsigned long get_bounce_buffer(struct lb_memory *mem)
+{
+	unsigned long lb_size;
+	unsigned long mem_entries;
+	unsigned long buffer;
+	int i;
+	lb_size = (unsigned long)(&_eram_seg - &_ram_seg);
+	/* Double linuxBIOS size so I have somewhere to place a copy to return to */
+	lb_size = lb_size + lb_size;
+	mem_entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]);
+	buffer = 0;
+	for(i = 0; i < mem_entries; i++) {
+		unsigned long mstart, mend;
+		unsigned long msize;
+		unsigned long tbuffer;
+		if (mem->map[i].type != LB_MEM_RAM)
+			continue;
+		if (mem->map[i].start > MAX_ADDR)
+			continue;
+		if (mem->map[i].size < lb_size)
+			continue;
+		mstart = mem->map[i].start;
+		msize = MAX_ADDR - mstart +1;
+		if (msize > mem->map[i].size)
+			msize = mem->map[i].size;
+		mend = mstart + msize;
+		tbuffer = mend - lb_size;
+		if (tbuffer < buffer) 
+			continue;
+		buffer = tbuffer;
+	}
+	return buffer;
+}
+
+
+static struct verify_callback *process_elf_notes(
+	unsigned char *header, 
+	unsigned long offset, unsigned long length)
+{
+	struct verify_callback *cb_chain;
+	unsigned char *note, *end;
+	char *program, *version;
+
+	cb_chain = 0;
+	note = header + offset;
+	end = note + length;
+	program = version = 0;
+	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_namesz == sizeof(ELF_NOTE_BOOT)) && 
+			(memcmp(n_name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT)) == 0)) {
+			switch(hdr->n_type) {
+			case EIN_PROGRAM_NAME:
+				if (n_desc[hdr->n_descsz -1] == 0) {
+					program = n_desc;
+				}
+				break;
+			case EIN_PROGRAM_VERSION:
+				if (n_desc[hdr->n_descsz -1] == 0) {
+					version = n_desc;
+				}
+				break;
+			case EIN_PROGRAM_CHECKSUM:
+			{
+				struct ip_checksum_vcb *cb;
+				cb = malloc(sizeof(*cb));
+				cb->ip_checksum = *((uint16_t *)n_desc);
+				cb->data.callback = verify_ip_checksum;
+				cb->data.next = cb_chain;
+				cb->data.desc_offset = n_desc - header;
+				cb_chain = &cb->data;
+				break;
+			}
+			}
+		}
+		printk_spew("n_type: %08x n_name(%d): %-*.*s n_desc(%d): %-*.*s\n", 
+			hdr->n_type,
+			hdr->n_namesz, hdr->n_namesz, hdr->n_namesz, n_name,
+			hdr->n_descsz,hdr->n_descsz, hdr->n_descsz, n_desc);
+		note = next;
+	}
+	if (program && version) {
+		printk_info("Loading %s version: %s\n",
+			program, version);
+	}
+	return cb_chain;
+}
+
+static int valid_area(struct lb_memory *mem, unsigned long buffer,
+	unsigned long start, unsigned long len)
+{
+	/* Check through all of the memory segments and ensure
+	 * the segment that was passed in is completely contained
+	 * in RAM.
+	 */
+	int i;
+	unsigned long end = start + len;
+	unsigned long mem_entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]);
+
+	/* See if I conflict with the bounce buffer */
+	if (end >= buffer) {
+		return 0;
+	}
+
+	/* Walk through the table of valid memory ranges and see if I
+	 * have a match.
+	 */
+	for(i = 0; i < mem_entries; i++) {
+		uint64_t mstart, mend;
+		uint32_t mtype;
+		mtype = mem->map[i].type;
+		mstart = mem->map[i].start;
+		mend = mstart + mem->map[i].size;
+		if ((mtype == LB_MEM_RAM) && (start < mend) && (end > mstart)) {
+			break;
+		}
+	}
+	if (i == mem_entries) {
+		printk_err("No matching ram area found for range:\n");
+		printk_err("  [0x%016lx, 0x%016lx)\n", start, end);
+		printk_err("Ram areas\n");
+		for(i = 0; i < mem_entries; i++) {
+			uint64_t mstart, mend;
+			uint32_t mtype;
+			mtype = mem->map[i].type;
+			mstart = mem->map[i].start;
+			mend = mstart + mem->map[i].size;
+			printk_err("  [0x%016lx, 0x%016lx) %s\n",
+				(unsigned long)mstart, 
+				(unsigned long)mend, 
+				(mtype == LB_MEM_RAM)?"RAM":"Reserved");
+			
+		}
+		return 0;
+	}
+	return 1;
+}
+
+static void relocate_segment(unsigned long buffer, struct segment *seg)
+{
+	/* Modify all segments that want to load onto linuxBIOS
+	 * to load onto the bounce buffer instead.
+	 */
+	unsigned long lb_start = (unsigned long)&_ram_seg;
+	unsigned long lb_end = (unsigned long)&_eram_seg;
+	unsigned long start, middle, end;
+
+	printk_spew("lb: [0x%016lx, 0x%016lx)\n", 
+		lb_start, lb_end);
+
+	start = seg->s_addr;
+	middle = start + seg->s_filesz;
+	end = start + seg->s_memsz;
+	/* I don't conflict with linuxBIOS so get out of here */
+	if ((end <= lb_start) || (start >= lb_end))
+		return;
+
+	printk_spew("segment: [0x%016lx, 0x%016lx, 0x%016lx)\n", 
+		start, middle, end);
+
+	/* Slice off a piece at the beginning
+	 * that doesn't conflict with linuxBIOS.
+	 */
+	if (start < lb_start) {
+		struct segment *new;
+		unsigned long len = lb_start - start;
+		new = malloc(sizeof(*new));
+		*new = *seg;
+		new->s_memsz = len;
+		seg->s_memsz -= len;
+		seg->s_addr += len;
+		seg->s_offset += len;
+		if (seg->s_filesz > len) {
+			new->s_filesz = len;
+			seg->s_filesz -= len;
+		} else {
+			seg->s_filesz = 0;
+		}
+
+		/* Order by stream offset */
+		new->next = seg;
+		new->prev = seg->prev;
+		seg->prev->next = new;
+		seg->prev = new;
+		/* Order by original program header order */
+		new->phdr_next = seg;
+		new->phdr_prev = seg->phdr_prev;
+		seg->phdr_prev->phdr_next = new;
+		seg->phdr_prev = new;
+
+		/* compute the new value of start */
+		start = seg->s_addr;
+		
+		printk_spew("   early: [0x%016lx, 0x%016lx, 0x%016lx)\n", 
+			new->s_addr, 
+			new->s_addr + new->s_filesz,
+			new->s_addr + new->s_memsz);
+	}
+	
+	/* Slice off a piece at the end 
+	 * that doesn't conflict with linuxBIOS 
+	 */
+	if (end > lb_end) {
+		unsigned long len = lb_end - start;
+		struct segment *new;
+		new = malloc(sizeof(*new));
+		*new = *seg;
+		seg->s_memsz = len;
+		new->s_memsz -= len;
+		new->s_addr += len;
+		new->s_offset += len;
+		if (seg->s_filesz > len) {
+			seg->s_filesz = len;
+			new->s_filesz -= len;
+		} else {
+			new->s_filesz = 0;
+		}
+		/* Order by stream offset */
+		new->next = seg->next;
+		new->prev = seg;
+		seg->next->prev = new;
+		seg->next = new;
+		/* Order by original program header order */
+		new->phdr_next = seg->phdr_next;
+		new->phdr_prev = seg;
+		seg->phdr_next->phdr_prev = new;
+		seg->phdr_next = new;
+
+		/* compute the new value of end */
+		end = start + len;
+		
+		printk_spew("   late: [0x%016lx, 0x%016lx, 0x%016lx)\n", 
+			new->s_addr, 
+			new->s_addr + new->s_filesz,
+			new->s_addr + new->s_memsz);
+		
+	}
+	/* Now retarget this segment onto the bounce buffer */
+	seg->s_addr = buffer + (seg->s_addr - lb_start);
+
+	printk_spew(" bounce: [0x%016lx, 0x%016lx, 0x%016lx)\n", 
+		seg->s_addr, 
+		seg->s_addr + seg->s_filesz, 
+		seg->s_addr + seg->s_memsz);
+}
+
+
+static int build_elf_segment_list(
+	struct segment *head, 
+	unsigned long bounce_buffer, struct lb_memory *mem,
+	Elf_phdr *phdr, int headers)
+{
+	struct segment *ptr;
+	int i;
+	memset(head, 0, sizeof(*head));
+	head->next = head->prev = head;
+	for(i = 0; i < headers; i++) {
+		struct segment *new;
+		/* Ignore data that I don't need to handle */
+		if (phdr[i].p_type != PT_LOAD) {
+			printk_debug("Dropping non PT_LOAD segment\n");
+			continue;
+		}
+		if (phdr[i].p_memsz == 0) {
+			printk_debug("Dropping empty segment\n");
+			continue;
+		}
+		new = malloc(sizeof(*new));
+		new->s_addr = phdr[i].p_paddr;
+		new->s_memsz = phdr[i].p_memsz;
+		new->s_offset = phdr[i].p_offset;
+		new->s_filesz = phdr[i].p_filesz;
+		printk_debug("New segment addr 0x%lx size 0x%lx offset 0x%lx filesize 0x%lx\n",
+			new->s_addr, new->s_memsz, new->s_offset, new->s_filesz);
+		/* Clean up the values */
+		if (new->s_filesz > new->s_memsz)  {
+			new->s_filesz = new->s_memsz;
+		}
+		printk_debug("(cleaned up) New segment addr 0x%lx size 0x%lx offset 0x%lx filesize 0x%lx\n",
+			new->s_addr, new->s_memsz, new->s_offset, new->s_filesz);
+		for(ptr = head->next; ptr != head; ptr = ptr->next) {
+			if (new->s_offset < ptr->s_offset)
+				break;
+		}
+		/* Order by stream offset */
+		new->next = ptr;
+		new->prev = ptr->prev;
+		ptr->prev->next = new;
+		ptr->prev = new;
+		/* Order by original program header order */
+		new->phdr_next = head;
+		new->phdr_prev = head->phdr_prev;
+		head->phdr_prev->phdr_next  = new;
+		head->phdr_prev = new;
+
+		/* Verify the memory addresses in the segment are valid */
+		if (!valid_area(mem, bounce_buffer, new->s_addr, new->s_memsz)) 
+			goto out;
+
+		/* Modify the segment to load onto the bounce_buffer if necessary.
+		 */
+		relocate_segment(bounce_buffer, new);
+	}
+	return 1;
+ out:
+	return 0;
+}
+
+static int load_elf_segments(
+	struct segment *head, unsigned char *header, unsigned long header_size)
+{
+	unsigned long offset;
+	struct segment *ptr;
+	
+	offset = 0;
+	for(ptr = head->next; ptr != head; ptr = ptr->next) {
+		unsigned long start_offset;
+		unsigned long skip_bytes, read_bytes;
+		unsigned char *dest, *middle, *end;
+		byte_offset_t result;
+		printk_debug("Loading Segment: addr: 0x%016lx memsz: 0x%016lx filesz: 0x%016lx\n",
+			ptr->s_addr, ptr->s_memsz, ptr->s_filesz);
+		
+		/* Compute the boundaries of the segment */
+		dest = (unsigned char *)(ptr->s_addr);
+		end = dest + ptr->s_memsz;
+		middle = dest + ptr->s_filesz;
+		start_offset = ptr->s_offset;
+		
+		printk_spew("[ 0x%016lx, %016lx, 0x%016lx) <- %016lx\n",
+			(unsigned long)dest,
+			(unsigned long)middle,
+			(unsigned long)end,
+			(unsigned long)start_offset);
+		
+		/* Skip intial buffer unused bytes */
+		if (offset < header_size) {
+			if (start_offset < header_size) {
+				offset = start_offset;
+			} else {
+				offset = header_size;
+			}
+		}
+		
+		/* Skip the unused bytes */
+		skip_bytes = start_offset - offset;
+		if (skip_bytes && 
+			((result = stream_skip(skip_bytes)) != skip_bytes)) {
+			printk_err("ERROR: Skip of %ld bytes skiped %ld bytes\n",
+				skip_bytes, result);
+			goto out;
+		}
+		offset = start_offset;
+		
+		/* Copy data from the initial buffer */
+		if (offset < header_size) {
+			size_t len;
+			if ((ptr->s_filesz + start_offset) > header_size) {
+				len = header_size - start_offset;
+			}
+			else {
+				len = ptr->s_filesz;
+			}
+			memcpy(dest, &header[start_offset], len);
+			dest += len;
+		}
+		
+		/* Read the segment into memory */
+		read_bytes = middle - dest;
+		if (read_bytes && 
+			((result = stream_read(dest, read_bytes)) != read_bytes)) {
+			printk_err("ERROR: Read of %ld bytes read %ld bytes...\n",
+				read_bytes, result);
+			goto out;
+		}
+		offset += ptr->s_filesz;
+		
+		/* Zero the extra bytes between middle & end */
+		if (middle < end) {
+			printk_debug("Clearing Segment: addr: 0x%016lx memsz: 0x%016lx\n",
+				(unsigned long)middle, end - middle);
+			
+			/* Zero the extra bytes */
+			memset(middle, 0, end - middle);
+		}
+	}
+	return 1;
+ out:
+	return 0;
+}
+
+static int verify_loaded_image(
+	struct verify_callback *vcb,
+	Elf_ehdr *ehdr, Elf_phdr *phdr,
+	struct segment *head
+	)
+{
+	struct segment *ptr;
+	int ok;
+	ok = 1;
+	for(; ok && vcb ; vcb = vcb->next) {
+		/* Find where the note is loaded */
+		/* The whole note must be loaded intact
+		 * so an address of 0 for the descriptor is impossible
+		 */
+		vcb->desc_addr = 0; 
+		for(ptr = head->next; ptr != head; ptr = ptr->next) {
+			unsigned long desc_addr;
+			desc_addr = ptr->s_addr + vcb->desc_offset - ptr->s_offset;
+			if ((desc_addr >= ptr->s_addr) &&
+				(desc_addr < (ptr->s_addr + ptr->s_filesz))) {
+				vcb->desc_addr = desc_addr;
+			}
+		}
+		ok = vcb->callback(vcb, ehdr, phdr, head);
+	}
+	return ok;
+}
+
+int elfload(struct lb_memory *mem,
+	unsigned char *header, unsigned long header_size)
+{
+	Elf_ehdr *ehdr;
+	Elf_phdr *phdr;
+	void *entry;
+	struct segment head;
+	struct verify_callback *cb_chain;
+	unsigned long bounce_buffer;
+
+	/* Find a bounce buffer so I can load to linuxBIOS's current location */
+	bounce_buffer = get_bounce_buffer(mem);
+	if (!bounce_buffer) {
+		printk_err("Could not find a bounce buffer...\n");
+		goto out;
+	}
+
+	ehdr = (Elf_ehdr *)header;
+	entry = (void *)(ehdr->e_entry);
+	phdr = (Elf_phdr *)(&header[ehdr->e_phoff]);
+
+	/* Digest elf note information... */
+	cb_chain = 0;
+	if ((phdr[0].p_type == PT_NOTE) && 
+		((phdr[0].p_offset + phdr[0].p_filesz) < header_size)) {
+		cb_chain = process_elf_notes(header,
+			phdr[0].p_offset, phdr[0].p_filesz);
+	}
+
+	/* Preprocess the elf segments */
+	if (!build_elf_segment_list(&head, 
+		bounce_buffer, mem, phdr, ehdr->e_phnum))
+		goto out;
+
+	/* Load the segments */
+	if (!load_elf_segments(&head, header, header_size))
+		goto out;
+
+	printk_spew("Loaded segments\n");
+	/* Verify the loaded image */
+	if (!verify_loaded_image(cb_chain, ehdr, phdr, &head)) 
+		goto out;
+
+	printk_spew("verified segments\n");
+	/* Shutdown the stream device */
+	stream_fini();
+	
+	printk_spew("closed down stream\n");
+	/* Reset to booting from this image as late as possible */
+	boot_successful();
+
+	printk_debug("Jumping to boot code at 0x%x\n", entry);
+	post_code(0xfe);
+
+	/* Jump to kernel */
+	jmp_to_elf_entry(entry, bounce_buffer);
+	return 1;
+
+ out:
+	return 0;
+}
+
+int elfboot(struct lb_memory *mem)
+{
+	Elf_ehdr *ehdr;
+	static unsigned char header[ELF_HEAD_SIZE];
+	int header_offset;
+	int i, result;
+
+	result = 0;
+	printk_info("\n");
+	printk_info("Welcome to %s, the open sourced starter.\n", BOOTLOADER);
+	printk_info("January 2002, Eric Biederman.\n");
+	printk_info("Version %s\n", BOOTLOADER_VERSION);
+	printk_info("\n");
+	post_code(0xf8);
+
+	if (stream_init() < 0) {
+		printk_err("Could not initialize driver...\n");
+		goto out;
+	}
+
+	/* Read in the initial ELF_HEAD_SIZE bytes */
+	if (stream_read(header, ELF_HEAD_SIZE) != ELF_HEAD_SIZE) {
+		printk_err("Read failed...\n");
+		goto out;
+	}
+	/* Scan for an elf header */
+	header_offset = -1;
+	for(i = 0; i < ELF_HEAD_SIZE - (sizeof(Elf_ehdr) + sizeof(Elf_phdr)); i+=16) {
+		ehdr = (Elf_ehdr *)(&header[i]);
+		if (memcmp(ehdr->e_ident, ELFMAG, 4) != 0) {
+			printk_spew("NO header at %d\n", i);
+			continue;
+		}
+		printk_debug("Found ELF candiate at offset %d\n", i);
+		/* Sanity check the elf header */
+		if ((ehdr->e_type == ET_EXEC) &&
+			elf_check_arch(ehdr) &&
+			(ehdr->e_ident[EI_VERSION] == EV_CURRENT) &&
+			(ehdr->e_version == EV_CURRENT) &&
+			(ehdr->e_ehsize == sizeof(Elf_ehdr)) &&
+			(ehdr->e_phentsize = sizeof(Elf_phdr)) &&
+			(ehdr->e_phoff < (ELF_HEAD_SIZE - i)) &&
+			((ehdr->e_phoff + (ehdr->e_phentsize * ehdr->e_phnum)) <= 
+				(ELF_HEAD_SIZE - i))) {
+			header_offset = i;
+			break;
+		}
+		ehdr = 0;
+	}
+	printk_spew("header_offset is %d\n", header_offset);
+	if (header_offset == -1) {
+		goto out;
+	}
+
+	printk_spew("Try to load at offset 0x%x\n", header_offset);
+	result = elfload(mem, 
+		header + header_offset , ELF_HEAD_SIZE - header_offset);
+ out:
+	if (!result) {
+		/* Shutdown the stream device */
+		stream_fini();
+
+		printk_err("Cannot Load ELF Image\n");
+
+		post_code(0xff);
+	}
+	return 0;
+
+}
diff --git a/src/boot/hardwaremain.c b/src/boot/hardwaremain.c
new file mode 100644
index 0000000..3507f88
--- /dev/null
+++ b/src/boot/hardwaremain.c
@@ -0,0 +1,216 @@
+/*
+This software and ancillary information (herein called SOFTWARE )
+called LinuxBIOS          is made available under the terms described
+here.  The SOFTWARE has been approved for release with associated
+LA-CC Number 00-34   .  Unless otherwise indicated, this SOFTWARE has
+been authored by an employee or employees of the University of
+California, operator of the Los Alamos National Laboratory under
+Contract No. W-7405-ENG-36 with the U.S. Department of Energy.  The
+U.S. Government has rights to use, reproduce, and distribute this
+SOFTWARE.  The public may copy, distribute, prepare derivative works
+and publicly display this SOFTWARE without charge, provided that this
+Notice and any statement of authorship are reproduced on all copies.
+Neither the Government nor the University makes any warranty, express
+or implied, or assumes any liability or responsibility for the use of
+this SOFTWARE.  If SOFTWARE is modified to produce derivative works,
+such modified SOFTWARE should be clearly marked, so as not to confuse
+it with the version available from LANL.
+ */
+/* Copyright 2000, Ron Minnich, Advanced Computing Lab, LANL
+ * rminnich@lanl.gov
+ */
+
+
+/*
+ * C Bootstrap code for the LinuxBIOS
+ */
+
+
+#include <console/console.h>
+#include <cpu/cpu.h>
+#include <mem.h>
+#include <version.h>
+#include <smp/start_stop.h>
+#include <boot/tables.h>
+#include <part/sizeram.h>
+#include <device.h>
+#include <pci.h>
+#if 0
+#include <part/mainboard.h>
+#endif
+#if 0
+#include <part/hard_reset.h>
+#endif
+#include <smp/atomic.h>
+#include <boot/elf.h>
+
+
+#ifndef CONFIG_MAX_PHYSICAL_CPUS
+#define CONFIG_MAX_PHYSICAL_CPUS CONFIG_MAX_CPUS
+#endif
+
+/* The processor map. 
+ * Now that SMP is in linuxbios, and Linux counts on us
+ * giving accurate information about processors, we need a map
+ * of what processors are out there. This could be a bit mask, 
+ * but we will be optimistic and hope we someday run on 
+ * REALLY BIG SMPs. Also we may need more than one bit of 
+ * info per processor at some point. I hope we don't need 
+ * anything more complex than an int.
+ */
+static unsigned long processor_map[MAX_CPUS];
+
+static struct mem_range *get_ramsize(void)
+{
+	struct mem_range *mem = 0;
+	if (!mem) {
+		mem = sizeram();
+	}
+	if (!mem) {
+		printk_err("No memory size information!\n");
+		for(;;);
+	}
+	return mem;
+}
+
+
+#if SMP == 1
+/* Number of cpus that are currently running in linuxbios */
+static atomic_t active_cpus = ATOMIC_INIT(1);
+
+void secondary_cpu_init(void)
+{
+	struct mem_range *mem;
+	unsigned long id;
+	int index;
+
+	atomic_inc(&active_cpus);
+	printk_debug(__FUNCTION__ "\n");
+	mem = get_ramsize();
+	id = cpu_initialize(mem);
+	index = processor_index(id);
+	printk_debug(__FUNCTION__ "  %d/%u\n", index, id);
+	processor_map[index] = CPU_ENABLED;
+	atomic_dec(&active_cpus);
+	stop_cpu(id);
+}
+
+static void wait_for_other_cpus(void)
+{
+	int old_active_count, active_count;
+	int i;
+	old_active_count = 1;
+
+	active_count = atomic_read(&active_cpus);
+	while(active_count > 1) {
+		if (active_count != old_active_count) {
+			printk_info("Waiting for %d CPUS to stop\n", active_count);
+			old_active_count = active_count;
+		}
+		active_count = atomic_read(&active_cpus);
+	}
+	for(i = 0; i < MAX_CPUS; i++) {
+		if (!(processor_map[i] & CPU_ENABLED)) {
+			printk_err("CPU %d/%u did not initialize!\n",
+				i, initial_apicid[i]);
+			processor_map[i] = 0;
+			mainboard_cpu_fixup(i);
+		}
+	}
+	printk_debug("All AP CPUs stopped\n");
+}
+
+#else /* SMP */
+#define wait_for_other_cpus() do {} while(0)
+#endif /* SMP */
+
+void hardwaremain(int boot_complete)
+{
+	/* Processor ID of the BOOT cpu (i.e. the one running this code) */
+	unsigned long boot_cpu;
+	int boot_index;
+
+	/* the order here is a bit tricky. We don't want to do much of 
+	 * anything that uses config registers until after PciAllocateResources
+	 * since that function also figures out what kind of config strategy
+	 * to use (type 1 or type 2). 
+	 * so we turn on cache, then worry about PCI setup, then do other 
+	 * things, so that the other work can use the PciRead* and PciWrite*
+	 * functions. 
+	 */
+	struct mem_range *mem, *tmem;
+	struct lb_memory *lb_mem;
+	unsigned long totalmem;
+
+	post_code(0x80);
+	/* displayinit MUST PRECEDE ALL PRINTK! */
+	console_init();
+	
+	post_code(0x39);
+	printk_notice("LinuxBIOS-%s%s %s %s...\n", 
+		linuxbios_version, linuxbios_extra_version, linuxbios_build,
+		(boot_complete)?"rebooting":"booting");
+
+	post_code(0x40);
+
+#if 0
+	/* If we have already booted attempt a hard reboot */
+	if (boot_complete) {
+		hard_reset();
+	}
+#endif
+#if 1
+
+	// pick how to scan the bus. This is first so we can get at memory size.
+	printk_info("Finding PCI configuration type.\n");
+	pci_set_method();
+	post_code(0x5f);
+#if 0
+	enumerate_static_devices();
+#endif
+	dev_enumerate();
+	post_code(0x66);
+	// Now do the real bus
+	// we round the total ram up a lot for thing like the SISFB, which 
+	// shares high memory with the CPU. 
+	dev_configure();
+	post_code(0x88);
+
+	dev_enable();
+	dev_initialize();
+	post_code(0x89);
+#endif
+
+	mem = get_ramsize();
+	post_code(0x70);
+	totalmem = 0;
+	for(tmem = mem; tmem->sizek; tmem++) {
+		totalmem += tmem->sizek;
+	}
+	printk_info("totalram: %ldM\n", 
+		(totalmem + 512) >> 10); /* Round to the nearest meg */
+
+	/* Fully initialize the cpu before configuring the bus */
+	boot_cpu = cpu_initialize(mem);
+	boot_index = processor_index(boot_cpu);
+	printk_spew("BOOT CPU is %d\n", boot_cpu);
+	processor_map[boot_index] = CPU_BOOTPROCESSOR|CPU_ENABLED;
+
+	/* Now start the other cpus initializing 
+	 * The sooner they start the sooner they stop.
+	 */
+	post_code(0x75);
+	startup_other_cpus(processor_map);
+	post_code(0x77);
+
+	/* make certain we are the only cpu running in linuxBIOS */
+	wait_for_other_cpus();
+
+	/* Now that we have collected all of our information
+	 * write our configuration tables.
+	 */
+	lb_mem = write_tables(mem, processor_map);
+
+	elfboot(lb_mem);
+}
+