From 8ca8d7665d671e10d72b8fcb4d69121d75f7906e Mon Sep 17 00:00:00 2001 From: Eric Biederman Date: Tue, 22 Apr 2003 19:02:15 +0000 Subject: - Initial checkin of the freebios2 tree git-svn-id: svn://svn.coreboot.org/coreboot/trunk@784 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1 --- src/arch/i386/boot/boot.c | 182 +++++++++++++++++++++++ src/arch/i386/boot/linuxbios_table.c | 281 +++++++++++++++++++++++++++++++++++ src/arch/i386/boot/linuxbios_table.h | 33 ++++ src/arch/i386/boot/pirq_routing.c | 93 ++++++++++++ src/arch/i386/boot/tables.c | 69 +++++++++ 5 files changed, 658 insertions(+) create mode 100644 src/arch/i386/boot/boot.c create mode 100644 src/arch/i386/boot/linuxbios_table.c create mode 100644 src/arch/i386/boot/linuxbios_table.h create mode 100644 src/arch/i386/boot/pirq_routing.c create mode 100644 src/arch/i386/boot/tables.c (limited to 'src/arch/i386/boot') diff --git a/src/arch/i386/boot/boot.c b/src/arch/i386/boot/boot.c new file mode 100644 index 0000000000..84c71da800 --- /dev/null +++ b/src/arch/i386/boot/boot.c @@ -0,0 +1,182 @@ +#include +#include +#include +#include +#include + + +#ifndef CMD_LINE +#define CMD_LINE "" +#endif + + + +#define UPSZ(X) ((sizeof(X) + 3) &~3) + +static struct { + Elf_Bhdr hdr; + Elf_Nhdr ft_hdr; + unsigned char ft_desc[UPSZ(FIRMWARE_TYPE)]; + Elf_Nhdr bl_hdr; + unsigned char bl_desc[UPSZ(BOOTLOADER)]; + Elf_Nhdr blv_hdr; + unsigned char blv_desc[UPSZ(BOOTLOADER_VERSION)]; + Elf_Nhdr cmd_hdr; + unsigned char cmd_desc[UPSZ(CMD_LINE)]; +} elf_boot_notes = { + .hdr = { + .b_signature = 0x0E1FB007, + .b_size = sizeof(elf_boot_notes), + .b_checksum = 0, + .b_records = 4, + }, + .ft_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(FIRMWARE_TYPE), + .n_type = EBN_FIRMWARE_TYPE, + }, + .ft_desc = FIRMWARE_TYPE, + .bl_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(BOOTLOADER), + .n_type = EBN_BOOTLOADER_NAME, + }, + .bl_desc = BOOTLOADER, + .blv_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(BOOTLOADER_VERSION), + .n_type = EBN_BOOTLOADER_VERSION, + }, + .blv_desc = BOOTLOADER_VERSION, + .cmd_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(CMD_LINE), + .n_type = EBN_COMMAND_LINE, + }, + .cmd_desc = CMD_LINE, +}; + + +int elf_check_arch(Elf_ehdr *ehdr) +{ + return ( + ((ehdr->e_machine == EM_386) || (ehdr->e_machine == EM_486)) && + (ehdr->e_ident[EI_CLASS] == ELFCLASS32) && + (ehdr->e_ident[EI_DATA] == ELFDATA2LSB) + ); + +} + +void jmp_to_elf_entry(void *entry, unsigned long buffer) +{ + extern unsigned char _ram_seg, _eram_seg; + unsigned long lb_start, lb_size; + unsigned long adjust, adjusted_boot_notes; + unsigned long type; + + elf_boot_notes.hdr.b_checksum = + compute_ip_checksum(&elf_boot_notes, sizeof(elf_boot_notes)); + + type = 0x0E1FB007; + lb_start = (unsigned long)&_ram_seg; + lb_size = (unsigned long)(&_eram_seg - &_ram_seg); + adjust = buffer + lb_size - lb_start; + + adjusted_boot_notes = (unsigned long)&elf_boot_notes; + adjusted_boot_notes += adjust; + + printk_spew("entry = 0x%08lx\n", (unsigned long)entry); + printk_spew("lb_start = 0x%08lx\n", lb_start); + printk_spew("lb_size = 0x%08lx\n", lb_size); + printk_spew("adjust = 0x%08lx\n", adjust); + printk_spew("buffer = 0x%08lx\n", buffer); + printk_spew(" elf_boot_notes = 0x%08lx\n", (unsigned long)&elf_boot_notes); + printk_spew("adjusted_boot_notes = 0x%08lx\n", adjusted_boot_notes); + + /* Jump to kernel */ + __asm__ __volatile__( + " cld \n\t" + /* Save the callee save registers... */ + " pushl %%esi\n\t" + " pushl %%edi\n\t" + " pushl %%ebx\n\t" + /* Save the parameters I was passed */ + " pushl $0\n\t" /* 20 adjust */ + " pushl %0\n\t" /* 16 lb_start */ + " pushl %1\n\t" /* 12 buffer */ + " pushl %2\n\t" /* 8 lb_size */ + " pushl %3\n\t" /* 4 entry */ + " pushl %4\n\t" /* 0 elf_boot_notes */ + /* Compute the adjustment */ + " xorl %%eax, %%eax\n\t" + " subl 16(%%esp), %%eax\n\t" + " addl 12(%%esp), %%eax\n\t" + " addl 8(%%esp), %%eax\n\t" + " movl %%eax, 20(%%esp)\n\t" + /* Place a copy of linuxBIOS in it's new location */ + /* Move ``longs'' the linuxBIOS size is 4 byte aligned */ + " movl 12(%%esp), %%edi\n\t" + " addl 8(%%esp), %%edi\n\t" + " movl 16(%%esp), %%esi\n\t" + " movl 8(%%esp), %%ecx\n\n" + " shrl $2, %%ecx\n\t" + " rep movsl\n\t" + + /* Adjust the stack pointer to point into the new linuxBIOS image */ + " addl 20(%%esp), %%esp\n\t" + /* Adjust the instruction pointer to point into the new linuxBIOS image */ + " movl $1f, %%eax\n\t" + " addl 20(%%esp), %%eax\n\t" + " jmp *%%eax\n\t" + "1: \n\t" + + /* Copy the linuxBIOS bounce buffer over linuxBIOS */ + /* Move ``longs'' the linuxBIOS size is 4 byte aligned */ + " movl 16(%%esp), %%edi\n\t" + " movl 12(%%esp), %%esi\n\t" + " movl 8(%%esp), %%ecx\n\t" + " shrl $2, %%ecx\n\t" + " rep movsl\n\t" + + /* Now jump to the loaded image */ + " movl $0x0E1FB007, %%eax\n\t" + " movl 0(%%esp), %%ebx\n\t" + " call *4(%%esp)\n\t" + + /* The loaded image returned? */ + " cli \n\t" + " cld \n\t" + + /* Copy the saved copy of linuxBIOS where linuxBIOS runs */ + /* Move ``longs'' the linuxBIOS size is 4 byte aligned */ + " movl 16(%%esp), %%edi\n\t" + " movl 12(%%esp), %%esi\n\t" + " addl 8(%%esp), %%esi\n\t" + " movl 8(%%esp), %%ecx\n\t" + " shrl $2, %%ecx\n\t" + " rep movsl\n\t" + + /* Adjust the stack pointer to point into the old linuxBIOS image */ + " subl 20(%%esp), %%esp\n\t" + + /* Adjust the instruction pointer to point into the old linuxBIOS image */ + " movl $1f, %%eax\n\t" + " subl 20(%%esp), %%eax\n\t" + " jmp *%%eax\n\t" + "1: \n\t" + + /* Drop the parameters I was passed */ + " addl $24, %%esp\n\t" + + /* Restore the callee save registers */ + " popl %%ebx\n\t" + " popl %%edi\n\t" + " popl %%esi\n\t" + + :: + "g" (lb_start), "g" (buffer), "g" (lb_size), + "g" (entry), "g"(adjusted_boot_notes) + ); +} + + diff --git a/src/arch/i386/boot/linuxbios_table.c b/src/arch/i386/boot/linuxbios_table.c new file mode 100644 index 0000000000..1925f2ddb5 --- /dev/null +++ b/src/arch/i386/boot/linuxbios_table.c @@ -0,0 +1,281 @@ +#include +#include +#include +#include +#include "linuxbios_table.h" +#include +#include + + +struct lb_header *lb_table_init(unsigned long addr) +{ + struct lb_header *header; + + /* 16 byte align the address */ + addr += 15; + addr &= ~15; + + header = (void *)addr; + header->signature[0] = 'L'; + header->signature[1] = 'B'; + header->signature[2] = 'I'; + header->signature[3] = 'O'; + header->header_bytes = sizeof(*header); + header->header_checksum = 0; + header->table_bytes = 0; + header->table_checksum = 0; + header->table_entries = 0; + return header; +} + +struct lb_record *lb_first_record(struct lb_header *header) +{ + struct lb_record *rec; + rec = (void *)(((char *)header) + sizeof(*header)); + return rec; +} + +struct lb_record *lb_last_record(struct lb_header *header) +{ + struct lb_record *rec; + rec = (void *)(((char *)header) + sizeof(*header) + header->table_bytes); + return rec; +} + +struct lb_record *lb_next_record(struct lb_record *rec) +{ + rec = (void *)(((char *)rec) + rec->size); + return rec; +} + +struct lb_record *lb_new_record(struct lb_header *header) +{ + struct lb_record *rec; + rec = lb_last_record(header); + if (header->table_entries) { + header->table_bytes += rec->size; + } + rec = lb_last_record(header); + header->table_entries++; + rec->tag = LB_TAG_UNUSED; + rec->size = sizeof(*rec); + return rec; +} + + +struct lb_memory *lb_memory(struct lb_header *header) +{ + struct lb_record *rec; + struct lb_memory *mem; + rec = lb_new_record(header); + mem = (struct lb_memory *)rec; + mem->tag = LB_TAG_MEMORY; + mem->size = sizeof(*mem); + return mem; +} + +struct lb_mainboard *lb_mainboard(struct lb_header *header) +{ + struct lb_record *rec; + struct lb_mainboard *mainboard; + rec = lb_new_record(header); + mainboard = (struct lb_mainboard *)rec; + mainboard->tag = LB_TAG_MAINBOARD; + + mainboard->size = (sizeof(*mainboard) + + strlen(mainboard_vendor) + 1 + + strlen(mainboard_part_number) + 1 + + 3) & ~3; + + mainboard->vendor_idx = 0; + mainboard->part_number_idx = strlen(mainboard_vendor) + 1; + + memcpy(mainboard->strings + mainboard->vendor_idx, + mainboard_vendor, strlen(mainboard_vendor) + 1); + memcpy(mainboard->strings + mainboard->part_number_idx, + mainboard_part_number, strlen(mainboard_part_number) + 1); + + return mainboard; +} + +void lb_strings(struct lb_header *header) +{ + static const struct { + uint32_t tag; + const uint8_t *string; + } strings[] = { + { LB_TAG_VERSION, linuxbios_version, }, + { LB_TAG_EXTRA_VERSION, linuxbios_extra_version, }, + { LB_TAG_BUILD, linuxbios_build, }, + { LB_TAG_COMPILE_TIME, linuxbios_compile_time, }, + { LB_TAG_COMPILE_BY, linuxbios_compile_by, }, + { LB_TAG_COMPILE_HOST, linuxbios_compile_host, }, + { LB_TAG_COMPILE_DOMAIN, linuxbios_compile_domain, }, + { LB_TAG_COMPILER, linuxbios_compiler, }, + { LB_TAG_LINKER, linuxbios_linker, }, + { LB_TAG_ASSEMBLER, linuxbios_assembler, }, + }; + int i; + for(i = 0; i < sizeof(strings)/sizeof(strings[0]); i++) { + struct lb_string *rec; + size_t len; + rec = (struct lb_string *)lb_new_record(header); + len = strlen(strings[i].string); + rec->tag = strings[i].tag; + rec->size = (sizeof(*rec) + len + 1 + 3) & ~3; + memcpy(rec->string, strings[i].string, len+1); + } + +} + +/* Some version of gcc have problems with 64 bit types so + * take an unsigned long instead of a uint64_t for now. + */ +void lb_memory_range(struct lb_memory *mem, + uint32_t type, unsigned long start, unsigned long size) +{ + int entries; + entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]); + mem->map[entries].start = start; + mem->map[entries].size = size; + mem->map[entries].type = type; + mem->size += sizeof(mem->map[0]); +} + +static void lb_memory_rangek(struct lb_memory *mem, + uint32_t type, unsigned long startk, unsigned long endk) +{ + int entries; + entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]); + mem->map[entries].start = startk; + mem->map[entries].start <<= 10; + mem->map[entries].size = endk - startk; + mem->map[entries].size <<= 10; + mem->map[entries].type = type; + mem->size += sizeof(mem->map[0]); +} + +static void lb_reserve_table_memory(struct lb_header *head) +{ + struct lb_record *last_rec; + struct lb_memory *mem; + uint64_t start; + uint64_t end; + int i, entries; + + last_rec = lb_last_record(head); + mem = get_lb_mem(); + start = (unsigned long)head; + end = (unsigned long)last_rec; + entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]); + /* Resize the right two memory areas so this table is in + * a reserved area of memory. Everything has been carefully + * setup so that is all we need to do. + */ + for(i = 0; i < entries; i++ ) { + uint64_t map_start = mem->map[i].start; + uint64_t map_end = map_start + mem->map[i].size; + /* Does this area need to be expanded? */ + if (map_end == start) { + mem->map[i].size = end - map_start; + } + /* Does this area need to be contracted? */ + else if (map_start == start) { + mem->map[i].start = end; + mem->map[i].size = map_end - end; + } + } +} + + +unsigned long lb_table_fini(struct lb_header *head) +{ + struct lb_record *rec, *first_rec; + rec = lb_last_record(head); + if (head->table_entries) { + head->table_bytes += rec->size; + } + lb_reserve_table_memory(head); + first_rec = lb_first_record(head); + head->table_checksum = compute_ip_checksum(first_rec, head->table_bytes); + head->header_checksum = 0; + head->header_checksum = compute_ip_checksum(head, sizeof(*head)); + printk_debug("Wrote linuxbios table at: %p - %p checksum %lx\n", + head, rec, head->table_checksum); + return (unsigned long)rec; +} + + +/* Routines to extract part so the linuxBIOS table or + * information from the linuxBIOS table after we have written it. + * Currently get_lb_mem relies on a global we can change the + * implementaiton. + */ +static struct lb_memory *mem_ranges = 0; +struct lb_memory *get_lb_mem(void) +{ + return mem_ranges; +} + +unsigned long write_linuxbios_table( + unsigned long *processor_map, + struct mem_range *ram, + unsigned long low_table_start, unsigned long low_table_end, + unsigned long rom_table_startk, unsigned long rom_table_endk) +{ + unsigned long table_size; + struct mem_range *ramp; + struct lb_header *head; + struct lb_memory *mem; + struct lb_record *rec_dest, *rec_src; + + head = lb_table_init(low_table_end); + low_table_end = (unsigned long)head; +#if HAVE_OPTION_TABLE == 1 + /* Write the option config table... */ + rec_dest = lb_new_record(head); + rec_src = (struct lb_record *)&option_table; + memcpy(rec_dest, rec_src, rec_src->size); +#endif + mem = lb_memory(head); + mem_ranges = mem; + /* I assume there is always ram at address 0 */ + /* Reserve our tables in low memory */ + table_size = (low_table_end - low_table_start); + lb_memory_range(mem, LB_MEM_TABLE, 0, table_size); + lb_memory_range(mem, LB_MEM_RAM, table_size, (ram[0].sizek << 10) - table_size); + /* Reserving pci memory mapped space will keep the kernel from booting seeing + * any pci resources. + */ + for(ramp = &ram[1]; ramp->sizek; ramp++) { + unsigned long startk, endk; + startk = ramp->basek; + endk = startk + ramp->sizek; + if ((startk < rom_table_startk) && (endk > rom_table_startk)) { + lb_memory_rangek(mem, LB_MEM_RAM, startk, rom_table_startk); + startk = rom_table_startk; + } + if ((startk == rom_table_startk) && (endk > startk)) { + unsigned long tend; + tend = rom_table_endk; + if (tend > endk) { + tend = endk; + } + lb_memory_rangek(mem, LB_MEM_TABLE, rom_table_startk, tend); + startk = tend; + } + if (endk > startk) { + lb_memory_rangek(mem, LB_MEM_RAM, startk, endk); + } + } + + /* Record our motheboard */ + lb_mainboard(head); + /* Record our various random string information */ + lb_strings(head); + + low_table_end = lb_table_fini(head); + + /* Remember where my valid memory ranges are */ + return low_table_end; +} diff --git a/src/arch/i386/boot/linuxbios_table.h b/src/arch/i386/boot/linuxbios_table.h new file mode 100644 index 0000000000..42c0a07dac --- /dev/null +++ b/src/arch/i386/boot/linuxbios_table.h @@ -0,0 +1,33 @@ +#ifndef LINUXBIOS_TABLE_H +#define LINUXBIOS_TABLE_H + +#include + +struct mem_range; + +/* This file holds function prototypes for building the linuxbios table. */ +unsigned long write_linuxbios_table( + unsigned long *processor_map, + struct mem_range *ram, + unsigned long low_table_start, unsigned long low_table_end, + unsigned long rom_table_start, unsigned long rom_table_end); + +struct lb_header *lb_table_init(unsigned long addr); +struct lb_record *lb_first_record(struct lb_header *header); +struct lb_record *lb_last_record(struct lb_header *header); +struct lb_record *lb_next_record(struct lb_record *rec); +struct lb_record *lb_new_record(struct lb_header *header); +struct lb_memory *lb_memory(struct lb_header *header); +void lb_memory_range(struct lb_memory *mem, + uint32_t type, unsigned long startk, unsigned long sizek); +struct lb_mainboard *lb_mainboard(struct lb_header *header); +unsigned long lb_table_fini(struct lb_header *header); + +/* Routines to extract part so the linuxBIOS table or information + * from the linuxBIOS table. + */ +struct lb_memory *get_lb_mem(void); + +extern struct cmos_option_table option_table; + +#endif /* LINUXBIOS_TABLE_H */ diff --git a/src/arch/i386/boot/pirq_routing.c b/src/arch/i386/boot/pirq_routing.c new file mode 100644 index 0000000000..a7325c42ac --- /dev/null +++ b/src/arch/i386/boot/pirq_routing.c @@ -0,0 +1,93 @@ +#include +#include +#include + +#ifdef DEBUG +void check_pirq_routing_table(void) +{ + const u8 *addr; + const struct irq_routing_table *rt; + int i; + u8 sum; + + printk_info("Checking IRQ routing tables...\n"); + +#ifdef(IRQ_SLOT_COUNT) + if (sizeof(intel_irq_routing_table) != intel_irq_routing_table.size) { + printk_warning("Inconsistent IRQ routing table size\n"); + } +#endif + + rt = &intel_irq_routing_table; + addr = (u8 *)rt; + + sum = 0; + for (i = 0; i < rt->size; i++) + sum += addr[i]; + + printk_debug("%s:%6d:%s() - irq_routing_table located at: 0x%p\n", + __FILE__, __LINE__, __FUNCTION__, addr); + + sum = (unsigned char)(rt->checksum-sum); + + if (sum != rt->checksum) { + printk_warning("%s:%6d:%s() - " + "checksum is: 0x%02x but should be: 0x%02x\n", + __FILE__, __LINE__, __FUNCTION__, rt->checksum, sum); + } + + if (rt->signature != PIRQ_SIGNATURE || rt->version != PIRQ_VERSION || + rt->size % 16 || rt->size < sizeof(struct irq_routing_table)) { + printk_warning("%s:%6d:%s() - " + "Interrupt Routing Table not valid\n", + __FILE__, __LINE__, __FUNCTION__); + return; + } + + sum = 0; + for (i=0; isize; i++) + sum += addr[i]; + + if (sum) { + printk_warning("%s:%6d:%s() - " + "checksum error in irq routing table\n", + __FILE__, __LINE__, __FUNCTION__); + } + + printk_info("done.\n"); +} + +int verify_copy_pirq_routing_table(unsigned long addr) +{ + int i; + u8 *rt_orig, *rt_curr; + + rt_curr = (u8*)addr; + rt_orig = (u8*)&intel_irq_routing_table; + printk_info("Verifing priq routing tables copy at 0x%x...", addr); + for (i = 0; i < intel_irq_routing_table.size; i++) { + if (*(rt_curr + i) != *(rt_orig + i)) { + printk_info("failed\n"); + return -1; + } + } + printk_info("succeed\n"); + return 0; +} +#else +#define verify_copy_pirq_routing_table(addr) +#endif + +unsigned long copy_pirq_routing_table(unsigned long addr) +{ + /* Align the table to be 16 byte aligned. */ + addr += 15; + addr &= ~15; + + /* This table must be betweeen 0xf0000 & 0x100000 */ + printk_info("Copying IRQ routing tables to 0x%x...", addr); + memcpy((void *)addr, &intel_irq_routing_table, intel_irq_routing_table.size); + printk_info("done.\n"); + verify_copy_pirq_routing_table(addr); + return addr + intel_irq_routing_table.size; +} diff --git a/src/arch/i386/boot/tables.c b/src/arch/i386/boot/tables.c new file mode 100644 index 0000000000..07579fefe2 --- /dev/null +++ b/src/arch/i386/boot/tables.c @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include +#include +#include +#include "linuxbios_table.h" + +#if CONFIG_SMP && CONFIG_MAX_PHYSICAL_CPUS && (CONFIG_MAX_PHYSICAL_CPUS < CONFIG_MAX_CPUS) +static void remove_logical_cpus(unsigned long *processor_map) +{ + /* To turn off hyperthreading just remove the logical + * cpus from the processor map. + */ + int disable_logical_cpus = !CONFIG_LOGICAL_CPUS; + if (get_option(&disable_logical_cpus,"hyper_threading")) { + disable_logical_cpus = !CONFIG_LOGICAL_CPUS; + } + if (disable_logical_cpus) { + /* disable logical cpus */ + int cnt; + for(cnt=MAX_PHYSICAL_CPUS;cnt> 10, rom_table_end >> 10); + + return get_lb_mem(); +} -- cgit v1.2.3