mirror of
https://github.com/fail0verflow/switch-coreboot.git
synced 2025-05-04 01:39:18 -04:00
add tables support to LinuxBIOS.
We have merged the plethora of include files into one. A given linuxbios target architecture must support all the functions described therein. All the structs etc. in include/tables.h are known to be architecture-independent. We hope this new layout is easier to folow than the old one. Todo: Remove the LGDT code from tables writing (how did THAT get in there ;-) and put it somewhere sane; add OFW table support. We are going to need some nice OFW table code. Also, the license headers should be correct in this commit. Signed-off-by: Ronald G. Minnich <rminnich@gmail.com> Acked-by: Ronald G. Minnich <rminnich@gmail.com> Acked-by: Stefan Reinauer <stepan@coresystems.de> git-svn-id: svn://coreboot.org/repository/LinuxBIOSv3@128 f3766cd6-281f-0410-b1cd-43a5c92072e9
This commit is contained in:
parent
aa91499a0d
commit
cd4625560e
4 changed files with 860 additions and 4 deletions
134
arch/x86/archtables.c
Normal file
134
arch/x86/archtables.c
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* table management code for Linux BIOS
|
||||
*
|
||||
*
|
||||
* Copright (C) 2002 Eric Biederman, Linux NetworX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that 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., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
/* 2006.1 yhlu add mptable cross 0x467 processing */
|
||||
|
||||
#include <console/console.h>
|
||||
//#include <cpu/cpu.h>
|
||||
//#include <boot/tables.h>
|
||||
//#include <boot/linuxbios_tables.h>
|
||||
//#include <arch/pirq_routing.h>
|
||||
//#include <arch/smp/mpspec.h>
|
||||
//#include <arch/acpi.h>
|
||||
#include <tables.h>
|
||||
|
||||
// Global Descriptor Table, defined in c_start.S
|
||||
extern uint8_t gdt;
|
||||
extern uint8_t gdt_end;
|
||||
|
||||
/* i386 lgdt argument */
|
||||
struct gdtarg {
|
||||
unsigned short limit;
|
||||
unsigned int base;
|
||||
} __attribute__((packed));
|
||||
|
||||
// Copy GDT to new location and reload it
|
||||
// 2003-07 by SONE Takeshi
|
||||
// Ported from Etherboot to LinuxBIOS 2005-08 by Steve Magnani
|
||||
void move_gdt(unsigned long newgdt)
|
||||
{
|
||||
uint16_t num_gdt_bytes = &gdt_end - &gdt;
|
||||
struct gdtarg gdtarg;
|
||||
|
||||
printk_debug("Moving GDT to %#lx...", newgdt);
|
||||
memcpy((void*)newgdt, &gdt, num_gdt_bytes);
|
||||
gdtarg.base = newgdt;
|
||||
gdtarg.limit = num_gdt_bytes - 1;
|
||||
__asm__ __volatile__ ("lgdt %0\n\t" : : "m" (gdtarg));
|
||||
printk_debug("ok\n");
|
||||
}
|
||||
|
||||
struct lb_memory *write_tables(void)
|
||||
{
|
||||
unsigned long low_table_start, low_table_end, new_low_table_end;
|
||||
unsigned long rom_table_start, rom_table_end;
|
||||
|
||||
rom_table_start = 0xf0000;
|
||||
rom_table_end = 0xf0000;
|
||||
/* Start low addr at 16 bytes instead of 0 because of a buglet
|
||||
* in the generic linux unzip code, as it tests for the a20 line.
|
||||
*/
|
||||
low_table_start = 0;
|
||||
low_table_end = 16;
|
||||
|
||||
post_code(0x9a);
|
||||
|
||||
/* This table must be betweeen 0xf0000 & 0x100000 */
|
||||
rom_table_end = write_pirq_routing_table(rom_table_end);
|
||||
rom_table_end = (rom_table_end + 1023) & ~1023;
|
||||
|
||||
/* Write ACPI tables */
|
||||
/* write them in the rom area because DSDT can be large (8K on epia-m) which
|
||||
* pushes linuxbios table out of first 4K if set up in low table area
|
||||
*/
|
||||
|
||||
rom_table_end = write_acpi_tables(rom_table_end);
|
||||
rom_table_end = (rom_table_end+1023) & ~1023;
|
||||
|
||||
/* copy the smp block to address 0 */
|
||||
post_code(0x96);
|
||||
|
||||
/* The smp table must be in 0-1K, 639K-640K, or 960K-1M */
|
||||
new_low_table_end = write_smp_table(low_table_end);
|
||||
|
||||
#if HAVE_MP_TABLE==1
|
||||
/* Don't write anything in the traditional x86 BIOS data segment,
|
||||
* for example the linux kernel smp need to use 0x467 to pass reset vector
|
||||
*/
|
||||
if(new_low_table_end>0x467){
|
||||
unsigned mptable_size = new_low_table_end - low_table_end - SMP_FLOATING_TABLE_LEN;
|
||||
/* We can not put mptable here, we need to copy them to somewhere else*/
|
||||
if((rom_table_end+mptable_size)<0x100000) {
|
||||
/* We can copy mptable on rom_table, and leave low space for lbtable */
|
||||
printk_debug("move mptable to 0x%0x\n", rom_table_end);
|
||||
memcpy((unsigned char *)rom_table_end, (unsigned char *)(low_table_end+SMP_FLOATING_TABLE_LEN), mptable_size);
|
||||
memset((unsigned char *)low_table_end, '\0', mptable_size + SMP_FLOATING_TABLE_LEN);
|
||||
smp_write_floating_table_physaddr(low_table_end, rom_table_end);
|
||||
low_table_end += SMP_FLOATING_TABLE_LEN;
|
||||
rom_table_end += mptable_size;
|
||||
rom_table_end = (rom_table_end+1023) & ~1023;
|
||||
} else {
|
||||
/* We can need to put mptable low and from 0x500 */
|
||||
printk_debug("move mptable to 0x%0x\n", 0x500);
|
||||
memcpy((unsigned char *)0x500, (unsigned char *)(low_table_end+SMP_FLOATING_TABLE_LEN), mptable_size);
|
||||
memset((unsigned char *)low_table_end, '\0', 0x500-low_table_end);
|
||||
smp_write_floating_table_physaddr(low_table_end, 0x500);
|
||||
low_table_end = 0x500 + mptable_size;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Don't write anything in the traditional x86 BIOS data segment */
|
||||
if (low_table_end < 0x500) {
|
||||
low_table_end = 0x500;
|
||||
}
|
||||
|
||||
// Relocate the GDT to reserved memory, so it won't get clobbered
|
||||
move_gdt(low_table_end);
|
||||
low_table_end += &gdt_end - &gdt;
|
||||
|
||||
/* The linuxbios table must be in 0-4K or 960K-1M */
|
||||
write_linuxbios_table(
|
||||
low_table_start, low_table_end,
|
||||
rom_table_start, rom_table_end);
|
||||
|
||||
return get_lb_mem();
|
||||
}
|
423
arch/x86/linuxbios_table.c
Normal file
423
arch/x86/linuxbios_table.c
Normal file
|
@ -0,0 +1,423 @@
|
|||
/*
|
||||
* table management code for Linux BIOS tables
|
||||
*
|
||||
*
|
||||
* Copright (C) 2002 Eric Biederman, Linux NetworX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that 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., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <console/console.h>
|
||||
//#include <ip_checksum.h>
|
||||
#include <tables.h>
|
||||
#include <string.h>
|
||||
#include <version.h>
|
||||
#include <device/device.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
struct cmos_checksum *lb_cmos_checksum(struct lb_header *header)
|
||||
{
|
||||
struct lb_record *rec;
|
||||
struct cmos_checksum *cmos_checksum;
|
||||
rec = lb_new_record(header);
|
||||
cmos_checksum = (struct cmos_checksum *)rec;
|
||||
cmos_checksum->tag = LB_TAG_OPTION_CHECKSUM;
|
||||
|
||||
cmos_checksum->size = (sizeof(*cmos_checksum));
|
||||
|
||||
cmos_checksum->range_start = LB_CKS_RANGE_START * 8;
|
||||
cmos_checksum->range_end = ( LB_CKS_RANGE_END * 8 ) + 7;
|
||||
cmos_checksum->location = LB_CKS_LOC * 8;
|
||||
cmos_checksum->type = CHECKSUM_PCBIOS;
|
||||
|
||||
return cmos_checksum;
|
||||
}
|
||||
|
||||
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, },
|
||||
};
|
||||
unsigned 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void lb_memory_range(struct lb_memory *mem,
|
||||
uint32_t type, uint64_t start, uint64_t size)
|
||||
{
|
||||
int entries;
|
||||
entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]);
|
||||
mem->map[entries].start = pack_lb64(start);
|
||||
mem->map[entries].size = pack_lb64(size);
|
||||
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 = unpack_lb64(mem->map[i].start);
|
||||
uint64_t map_end = map_start + unpack_lb64(mem->map[i].size);
|
||||
/* Does this area need to be expanded? */
|
||||
if (map_end == start) {
|
||||
mem->map[i].size = pack_lb64(end - map_start);
|
||||
}
|
||||
/* Does this area need to be contracted? */
|
||||
else if (map_start == start) {
|
||||
mem->map[i].start = pack_lb64(end);
|
||||
mem->map[i].size = pack_lb64(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 = 0; //compute_ip_checksum(first_rec, head->table_bytes);
|
||||
head->header_checksum = 0;
|
||||
head->header_checksum = 0; //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;
|
||||
}
|
||||
|
||||
static void lb_cleanup_memory_ranges(struct lb_memory *mem)
|
||||
{
|
||||
int entries;
|
||||
int i, j;
|
||||
entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]);
|
||||
|
||||
/* Sort the lb memory ranges */
|
||||
for(i = 0; i < entries; i++) {
|
||||
uint64_t entry_start = unpack_lb64(mem->map[i].start);
|
||||
for(j = i; j < entries; j++) {
|
||||
uint64_t temp_start = unpack_lb64(mem->map[j].start);
|
||||
if (temp_start < entry_start) {
|
||||
struct lb_memory_range tmp;
|
||||
tmp = mem->map[i];
|
||||
mem->map[i] = mem->map[j];
|
||||
mem->map[j] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Merge adjacent entries */
|
||||
for(i = 0; (i + 1) < entries; i++) {
|
||||
uint64_t start, end, nstart, nend;
|
||||
if (mem->map[i].type != mem->map[i + 1].type) {
|
||||
continue;
|
||||
}
|
||||
start = unpack_lb64(mem->map[i].start);
|
||||
end = start + unpack_lb64(mem->map[i].size);
|
||||
nstart = unpack_lb64(mem->map[i + 1].start);
|
||||
nend = nstart + unpack_lb64(mem->map[i + 1].size);
|
||||
if ((start <= nstart) && (end > nstart)) {
|
||||
if (start > nstart) {
|
||||
start = nstart;
|
||||
}
|
||||
if (end < nend) {
|
||||
end = nend;
|
||||
}
|
||||
/* Record the new region size */
|
||||
mem->map[i].start = pack_lb64(start);
|
||||
mem->map[i].size = pack_lb64(end - start);
|
||||
|
||||
/* Delete the entry I have merged with */
|
||||
memmove(&mem->map[i + 1], &mem->map[i + 2],
|
||||
((entries - i - 2) * sizeof(mem->map[0])));
|
||||
mem->size -= sizeof(mem->map[0]);
|
||||
entries -= 1;
|
||||
/* See if I can merge with the next entry as well */
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void lb_remove_memory_range(struct lb_memory *mem,
|
||||
uint64_t start, uint64_t size)
|
||||
{
|
||||
uint64_t end;
|
||||
int entries;
|
||||
int i;
|
||||
|
||||
end = start + size;
|
||||
entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]);
|
||||
|
||||
/* Remove a reserved area from the memory map */
|
||||
for(i = 0; i < entries; i++) {
|
||||
uint64_t map_start = unpack_lb64(mem->map[i].start);
|
||||
uint64_t map_end = map_start + unpack_lb64(mem->map[i].size);
|
||||
if ((start <= map_start) && (end >= map_end)) {
|
||||
/* Remove the completely covered range */
|
||||
memmove(&mem->map[i], &mem->map[i + 1],
|
||||
((entries - i - 1) * sizeof(mem->map[0])));
|
||||
mem->size -= sizeof(mem->map[0]);
|
||||
entries -= 1;
|
||||
/* Since the index will disappear revisit what will appear here */
|
||||
i -= 1;
|
||||
}
|
||||
else if ((start > map_start) && (end < map_end)) {
|
||||
/* Split the memory range */
|
||||
memmove(&mem->map[i + 1], &mem->map[i],
|
||||
((entries - i) * sizeof(mem->map[0])));
|
||||
mem->size += sizeof(mem->map[0]);
|
||||
entries += 1;
|
||||
/* Update the first map entry */
|
||||
mem->map[i].size = pack_lb64(start - map_start);
|
||||
/* Update the second map entry */
|
||||
mem->map[i + 1].start = pack_lb64(end);
|
||||
mem->map[i + 1].size = pack_lb64(map_end - end);
|
||||
/* Don't bother with this map entry again */
|
||||
i += 1;
|
||||
}
|
||||
else if ((start <= map_start) && (end > map_start)) {
|
||||
/* Shrink the start of the memory range */
|
||||
mem->map[i].start = pack_lb64(end);
|
||||
mem->map[i].size = pack_lb64(map_end - end);
|
||||
}
|
||||
else if ((start < map_end) && (start > map_start)) {
|
||||
/* Shrink the end of the memory range */
|
||||
mem->map[i].size = pack_lb64(start - map_start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void lb_add_memory_range(struct lb_memory *mem,
|
||||
uint32_t type, uint64_t start, uint64_t size)
|
||||
{
|
||||
lb_remove_memory_range(mem, start, size);
|
||||
lb_memory_range(mem, type, start, size);
|
||||
lb_cleanup_memory_ranges(mem);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
static void build_lb_mem_range(void *gp, struct device *dev, struct resource *res)
|
||||
{
|
||||
struct lb_memory *mem = gp;
|
||||
lb_memory_range(mem, LB_MEM_RAM, res->base, res->size);
|
||||
}
|
||||
|
||||
static struct lb_memory *build_lb_mem(struct lb_header *head)
|
||||
{
|
||||
struct lb_memory *mem;
|
||||
|
||||
/* Record where the lb memory ranges will live */
|
||||
mem = lb_memory(head);
|
||||
mem_ranges = mem;
|
||||
|
||||
/* Build the raw table of memory */
|
||||
search_global_resources(
|
||||
IORESOURCE_MEM | IORESOURCE_CACHEABLE, IORESOURCE_MEM | IORESOURCE_CACHEABLE,
|
||||
build_lb_mem_range, mem);
|
||||
lb_cleanup_memory_ranges(mem);
|
||||
return mem;
|
||||
}
|
||||
|
||||
unsigned long write_linuxbios_table(
|
||||
unsigned long low_table_start, unsigned long low_table_end,
|
||||
unsigned long rom_table_start, unsigned long rom_table_end)
|
||||
{
|
||||
unsigned long table_size;
|
||||
struct lb_header *head;
|
||||
struct lb_memory *mem;
|
||||
|
||||
if(low_table_end > (0x1000 - sizeof(struct lb_header))) { /* after 4K */
|
||||
/* We need to put lbtable on to [0xf0000,0x100000) */
|
||||
head = lb_table_init(rom_table_end);
|
||||
rom_table_end = (unsigned long)head;
|
||||
} else {
|
||||
head = lb_table_init(low_table_end);
|
||||
low_table_end = (unsigned long)head;
|
||||
}
|
||||
|
||||
if (HAVE_OPTION_TABLE == 1) {
|
||||
struct lb_record *rec_dest, *rec_src;
|
||||
/* Write the option config table... */
|
||||
rec_dest = lb_new_record(head);
|
||||
rec_src = (struct lb_record *)(void *)&option_table;
|
||||
memcpy(rec_dest, rec_src, rec_src->size);
|
||||
/* Create cmos checksum entry in linuxbios table */
|
||||
lb_cmos_checksum(head);
|
||||
}
|
||||
/* Record where RAM is located */
|
||||
mem = build_lb_mem(head);
|
||||
|
||||
/* Record the mptable and the the lb_table (This will be adjusted later) */
|
||||
lb_add_memory_range(mem, LB_MEM_TABLE,
|
||||
low_table_start, low_table_end - low_table_start);
|
||||
|
||||
/* Record the pirq table, acpi tables, and maybe the mptable */
|
||||
lb_add_memory_range(mem, LB_MEM_TABLE,
|
||||
rom_table_start, rom_table_end - rom_table_start);
|
||||
|
||||
/* Note:
|
||||
* I assume that there is always memory at immediately after
|
||||
* the low_table_end. This means that after I setup the linuxbios table.
|
||||
* I can trivially fixup the reserved memory ranges to hold the correct
|
||||
* size of the linuxbios table.
|
||||
*/
|
||||
|
||||
/* Record our motheboard */
|
||||
lb_mainboard(head);
|
||||
/* Record our various random string information */
|
||||
lb_strings(head);
|
||||
|
||||
/* Remember where my valid memory ranges are */
|
||||
return lb_table_fini(head);
|
||||
|
||||
}
|
260
include/tables.h
260
include/tables.h
|
@ -1,8 +1,260 @@
|
|||
#ifndef BOOT_TABLES_H
|
||||
#define BOOT_TABLES_H
|
||||
/*
|
||||
* table management structs and prototypes for LinuxBIOS
|
||||
* ALL structs and prototypes for tables that LinuxBIOS generates should be defined here.
|
||||
*
|
||||
*
|
||||
* Copright (C) 2002 Eric Biederman, Linux NetworX
|
||||
* Copright (C) 2005 Stefan Reinauer, Core Systems
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that 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., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <boot/linuxbios_tables.h>
|
||||
#ifndef TABLES_H
|
||||
#define TABLES_H
|
||||
|
||||
struct lb_memory *write_tables(void);
|
||||
|
||||
#endif /* BOOT_TABLES_H */
|
||||
/* The linuxbios table information is for conveying information
|
||||
* from the firmware to the loaded OS image. Primarily this
|
||||
* is expected to be information that cannot be discovered by
|
||||
* other means, such as quering the hardware directly.
|
||||
*
|
||||
* All of the information should be Position Independent Data.
|
||||
* That is it should be safe to relocated any of the information
|
||||
* without it's meaning/correctnes changing. For table that
|
||||
* can reasonably be used on multiple architectures the data
|
||||
* size should be fixed. This should ease the transition between
|
||||
* 32 bit and 64 bit architectures etc.
|
||||
*
|
||||
* The completeness test for the information in this table is:
|
||||
* - Can all of the hardware be detected?
|
||||
* - Are the per motherboard constants available?
|
||||
* - Is there enough to allow a kernel to run that was written before
|
||||
* a particular motherboard is constructed? (Assuming the kernel
|
||||
* has drivers for all of the hardware but it does not have
|
||||
* assumptions on how the hardware is connected together).
|
||||
*
|
||||
* With this test it should be straight forward to determine if a
|
||||
* table entry is required or not. This should remove much of the
|
||||
* long term compatibility burden as table entries which are
|
||||
* irrelevant or have been replaced by better alternatives may be
|
||||
* dropped. Of course it is polite and expidite to include extra
|
||||
* table entries and be backwards compatible, but it is not required.
|
||||
*/
|
||||
|
||||
/* Since LinuxBIOS is usually compiled 32bit, gcc will align 64bit
|
||||
* types to 32bit boundaries. If the LinuxBIOS table is dumped on a
|
||||
* 64bit system, a uint64_t would be aligned to 64bit boundaries,
|
||||
* breaking the table format.
|
||||
*
|
||||
* lb_uint64 will keep 64bit LinuxBIOS table values aligned to 32bit
|
||||
* to ensure compatibility. They can be accessed with the two functions
|
||||
* below: unpack_lb64() and pack_lb64()
|
||||
*
|
||||
* See also: util/lbtdump/lbtdump.c
|
||||
*/
|
||||
|
||||
struct lb_uint64 {
|
||||
uint32_t lo;
|
||||
uint32_t hi;
|
||||
};
|
||||
|
||||
static inline uint64_t unpack_lb64(struct lb_uint64 value)
|
||||
{
|
||||
uint64_t result;
|
||||
result = value.hi;
|
||||
result = (result << 32) + value.lo;
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline struct lb_uint64 pack_lb64(uint64_t value)
|
||||
{
|
||||
struct lb_uint64 result;
|
||||
result.lo = (value >> 0) & 0xffffffff;
|
||||
result.hi = (value >> 32) & 0xffffffff;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct lb_header
|
||||
{
|
||||
uint8_t signature[4]; /* LBIO */
|
||||
uint32_t header_bytes;
|
||||
uint32_t header_checksum;
|
||||
uint32_t table_bytes;
|
||||
uint32_t table_checksum;
|
||||
uint32_t table_entries;
|
||||
};
|
||||
|
||||
/* Every entry in the boot enviroment list will correspond to a boot
|
||||
* info record. Encoding both type and size. The type is obviously
|
||||
* so you can tell what it is. The size allows you to skip that
|
||||
* boot enviroment record if you don't know what it easy. This allows
|
||||
* forward compatibility with records not yet defined.
|
||||
*/
|
||||
struct lb_record {
|
||||
uint32_t tag; /* tag ID */
|
||||
uint32_t size; /* size of record (in bytes) */
|
||||
};
|
||||
|
||||
#define LB_TAG_UNUSED 0x0000
|
||||
|
||||
#define LB_TAG_MEMORY 0x0001
|
||||
|
||||
struct lb_memory_range {
|
||||
struct lb_uint64 start;
|
||||
struct lb_uint64 size;
|
||||
uint32_t type;
|
||||
#define LB_MEM_RAM 1 /* Memory anyone can use */
|
||||
#define LB_MEM_RESERVED 2 /* Don't use this memory region */
|
||||
#define LB_MEM_TABLE 16 /* Ram configuration tables are kept in */
|
||||
};
|
||||
|
||||
struct lb_memory {
|
||||
uint32_t tag;
|
||||
uint32_t size;
|
||||
struct lb_memory_range map[0];
|
||||
};
|
||||
|
||||
#define LB_TAG_HWRPB 0x0002
|
||||
struct lb_hwrpb {
|
||||
uint32_t tag;
|
||||
uint32_t size;
|
||||
uint64_t hwrpb;
|
||||
};
|
||||
|
||||
#define LB_TAG_MAINBOARD 0x0003
|
||||
struct lb_mainboard {
|
||||
uint32_t tag;
|
||||
uint32_t size;
|
||||
uint8_t vendor_idx;
|
||||
uint8_t part_number_idx;
|
||||
uint8_t strings[0];
|
||||
};
|
||||
|
||||
#define LB_TAG_VERSION 0x0004
|
||||
#define LB_TAG_EXTRA_VERSION 0x0005
|
||||
#define LB_TAG_BUILD 0x0006
|
||||
#define LB_TAG_COMPILE_TIME 0x0007
|
||||
#define LB_TAG_COMPILE_BY 0x0008
|
||||
#define LB_TAG_COMPILE_HOST 0x0009
|
||||
#define LB_TAG_COMPILE_DOMAIN 0x000a
|
||||
#define LB_TAG_COMPILER 0x000b
|
||||
#define LB_TAG_LINKER 0x000c
|
||||
#define LB_TAG_ASSEMBLER 0x000d
|
||||
struct lb_string {
|
||||
uint32_t tag;
|
||||
uint32_t size;
|
||||
uint8_t string[0];
|
||||
};
|
||||
|
||||
/* The following structures are for the cmos definitions table */
|
||||
#define LB_TAG_CMOS_OPTION_TABLE 200
|
||||
/* cmos header record */
|
||||
struct cmos_option_table {
|
||||
uint32_t tag; /* CMOS definitions table type */
|
||||
uint32_t size; /* size of the entire table */
|
||||
uint32_t header_length; /* length of header */
|
||||
};
|
||||
|
||||
/* cmos entry record
|
||||
This record is variable length. The name field may be
|
||||
shorter than CMOS_MAX_NAME_LENGTH. The entry may start
|
||||
anywhere in the byte, but can not span bytes unless it
|
||||
starts at the beginning of the byte and the length is
|
||||
fills complete bytes.
|
||||
*/
|
||||
#define LB_TAG_OPTION 201
|
||||
struct cmos_entries {
|
||||
uint32_t tag; /* entry type */
|
||||
uint32_t size; /* length of this record */
|
||||
uint32_t bit; /* starting bit from start of image */
|
||||
uint32_t length; /* length of field in bits */
|
||||
uint32_t config; /* e=enumeration, h=hex, r=reserved */
|
||||
uint32_t config_id; /* a number linking to an enumeration record */
|
||||
#define CMOS_MAX_NAME_LENGTH 32
|
||||
uint8_t name[CMOS_MAX_NAME_LENGTH]; /* name of entry in ascii,
|
||||
variable length int aligned */
|
||||
};
|
||||
|
||||
|
||||
/* cmos enumerations record
|
||||
This record is variable length. The text field may be
|
||||
shorter than CMOS_MAX_TEXT_LENGTH.
|
||||
*/
|
||||
#define LB_TAG_OPTION_ENUM 202
|
||||
struct cmos_enums {
|
||||
uint32_t tag; /* enumeration type */
|
||||
uint32_t size; /* length of this record */
|
||||
uint32_t config_id; /* a number identifying the config id */
|
||||
uint32_t value; /* the value associated with the text */
|
||||
#define CMOS_MAX_TEXT_LENGTH 32
|
||||
uint8_t text[CMOS_MAX_TEXT_LENGTH]; /* enum description in ascii,
|
||||
variable length int aligned */
|
||||
};
|
||||
|
||||
/* cmos defaults record
|
||||
This record contains default settings for the cmos ram.
|
||||
*/
|
||||
#define LB_TAG_OPTION_DEFAULTS 203
|
||||
struct cmos_defaults {
|
||||
uint32_t tag; /* default type */
|
||||
uint32_t size; /* length of this record */
|
||||
uint32_t name_length; /* length of the following name field */
|
||||
uint8_t name[CMOS_MAX_NAME_LENGTH]; /* name identifying the default */
|
||||
#define CMOS_IMAGE_BUFFER_SIZE 128
|
||||
uint8_t default_set[CMOS_IMAGE_BUFFER_SIZE]; /* default settings */
|
||||
};
|
||||
|
||||
#define LB_TAG_OPTION_CHECKSUM 204
|
||||
struct cmos_checksum {
|
||||
uint32_t tag;
|
||||
uint32_t size;
|
||||
/* In practice everything is byte aligned, but things are measured
|
||||
* in bits to be consistent.
|
||||
*/
|
||||
uint32_t range_start; /* First bit that is checksummed (byte aligned) */
|
||||
uint32_t range_end; /* Last bit that is checksummed (byte aligned) */
|
||||
uint32_t location; /* First bit of the checksum (byte aligned) */
|
||||
uint32_t type; /* Checksum algorithm that is used */
|
||||
#define CHECKSUM_NONE 0
|
||||
#define CHECKSUM_PCBIOS 1
|
||||
};
|
||||
|
||||
unsigned long write_linuxbios_table(
|
||||
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, uint64_t start, uint64_t size);
|
||||
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 /* TABLES_H */
|
||||
|
|
47
lib/tables.c
Normal file
47
lib/tables.c
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* table management code for Linux BIOS
|
||||
* This is the architecture-independent driver; it has a hook to architecture-dependent code.
|
||||
*
|
||||
* Copright (C) 2002 Eric Biederman, Linux NetworX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that 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., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <console/console.h>
|
||||
#include <cpu.h>
|
||||
#include <tables.h>
|
||||
#include <boot/linuxbios_tables.h>
|
||||
|
||||
struct lb_memory *
|
||||
write_tables(void)
|
||||
{
|
||||
unsigned long low_table_start, low_table_end;
|
||||
unsigned long rom_table_start, rom_table_end;
|
||||
|
||||
rom_table_start = 0xf0000;
|
||||
rom_table_end = 0xf0000;
|
||||
/* Start low addr at 16 bytes instead of 0 because of a buglet
|
||||
* in the generic linux unzip code, as it tests for the a20 line.
|
||||
*/
|
||||
low_table_start = 0;
|
||||
low_table_end = 16;
|
||||
|
||||
/* The linuxbios table must be in 0-4K or 960K-1M */
|
||||
write_linuxbios_table(
|
||||
low_table_start, low_table_end,
|
||||
rom_table_start, rom_table_end);
|
||||
|
||||
return get_lb_mem();
|
||||
}
|
Loading…
Add table
Reference in a new issue