blob: 9b13d6f9a8258980e720966597a3d36e4cf1d056 [file] [log] [blame]
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2003-2004 Eric Biederman
* Copyright (C) 2005-2010 coresystems GmbH
*
* 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 <parlib/common.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <vmm/coreboot_tables.h>
static struct lb_header *lb_table_init(void *addr)
{
struct lb_header *header;
/* 16 byte align the address */
header = (void *)(((unsigned long)addr + 15) & ~15);
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;
}
static struct lb_record *lb_first_record(struct lb_header *header)
{
struct lb_record *rec;
rec = (void *)(((char *)header) + sizeof(*header));
return rec;
}
static 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_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;
}
static 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;
}
void lb_add_serial(struct lb_serial *new_serial, void *data)
{
struct lb_header *header = (struct lb_header *)data;
struct lb_serial *serial;
serial = (struct lb_serial *)lb_new_record(header);
serial->tag = LB_TAG_SERIAL;
serial->size = sizeof(*serial);
serial->type = new_serial->type;
serial->baseaddr = new_serial->baseaddr;
serial->baud = new_serial->baud;
serial->regwidth = new_serial->regwidth;
}
void lb_add_console(uint16_t consoletype, void *data)
{
struct lb_header *header = (struct lb_header *)data;
struct lb_console *console;
console = (struct lb_console *)lb_new_record(header);
console->tag = LB_TAG_CONSOLE;
console->size = sizeof(*console);
console->type = consoletype;
}
static void lb_framebuffer(struct lb_header *header)
{
#if 0 //CONFIG_FRAMEBUFFER_KEEP_VESA_MODE || CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT
void fill_lb_framebuffer(struct lb_framebuffer *framebuffer);
int vbe_mode_info_valid(void);
// If there isn't any mode info to put in the table, don't ask for it
// to be filled with junk.
if (!vbe_mode_info_valid())
return;
struct lb_framebuffer *framebuffer;
framebuffer = (struct lb_framebuffer *)lb_new_record(header);
fill_lb_framebuffer(framebuffer);
framebuffer->tag = LB_TAG_FRAMEBUFFER;
framebuffer->size = sizeof(*framebuffer);
#endif
}
void fill_lb_gpio(struct lb_gpio *gpio, int num,
int polarity, const char *name, int value)
{
memset(gpio, 0, sizeof(*gpio));
gpio->port = num;
gpio->polarity = polarity;
if (value >= 0)
gpio->value = value;
strncpy((char *)gpio->name, name, GPIO_MAX_NAME_LENGTH);
}
#if CONFIG_CHROMEOS
static void lb_gpios(struct lb_header *header)
{
struct lb_gpios *gpios;
gpios = (struct lb_gpios *)lb_new_record(header);
gpios->tag = LB_TAG_GPIO;
gpios->size = sizeof(*gpios);
gpios->count = 0;
fill_lb_gpios(gpios);
}
static void lb_vdat(struct lb_header *header)
{
#if CONFIG_HAVE_ACPI_TABLES
struct lb_range *vdat;
vdat = (struct lb_range *)lb_new_record(header);
vdat->tag = LB_TAG_VDAT;
vdat->size = sizeof(*vdat);
acpi_get_vdat_info(&vdat->range_start, &vdat->range_size);
#endif
}
static void lb_vbnv(struct lb_header *header)
{
#if CONFIG_PC80_SYSTEM
struct lb_range *vbnv;
vbnv = (struct lb_range *)lb_new_record(header);
vbnv->tag = LB_TAG_VBNV;
vbnv->size = sizeof(*vbnv);
vbnv->range_start = CONFIG_VBNV_OFFSET + 14;
vbnv->range_size = CONFIG_VBNV_SIZE;
#endif
}
#if CONFIG_VBOOT_VERIFY_FIRMWARE
static void lb_vboot_handoff(struct lb_header *header)
{
void *addr;
uint32_t size;
struct lb_range *vbho;
if (vboot_get_handoff_info(&addr, &size))
return;
vbho = (struct lb_range *)lb_new_record(header);
vbho->tag = LB_TAB_VBOOT_HANDOFF;
vbho->size = sizeof(*vbho);
vbho->range_start = (intptr_t)addr;
vbho->range_size = size;
}
#else
static inline void lb_vboot_handoff(struct lb_header *header) {}
#endif /* CONFIG_VBOOT_VERIFY_FIRMWARE */
#endif /* CONFIG_CHROMEOS */
static void lb_board_id(struct lb_header *header)
{
#if 0 // CONFIG_BOARD_ID_AUTO || CONFIG_BOARD_ID_MANUAL
struct lb_board_id *bid;
bid = (struct lb_board_id *)lb_new_record(header);
bid->tag = LB_TAG_BOARD_ID;
bid->size = sizeof(*bid);
bid->board_id = board_id();
#endif
}
static void lb_strings(struct lb_header *header)
{
static const struct {
uint32_t tag;
const char *string;
} strings[] = {
// we may want this at some point.
};
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);
}
}
static void lb_record_version_timestamp(struct lb_header *header)
{
struct lb_timestamp *rec;
rec = (struct lb_timestamp *)lb_new_record(header);
rec->tag = LB_TAG_VERSION_TIMESTAMP;
rec->size = sizeof(*rec);
// TODO
//rec->timestamp = coreboot_version_timestamp;
}
void __attribute__((weak)) lb_board(struct lb_header *header) { /* NOOP */ }
static struct lb_forward *lb_forward(struct lb_header *header, struct lb_header *next_header)
{
struct lb_record *rec;
struct lb_forward *forward;
rec = lb_new_record(header);
forward = (struct lb_forward *)rec;
forward->tag = LB_TAG_FORWARD;
forward->size = sizeof(*forward);
forward->forward = (uint64_t)(unsigned long)next_header;
return forward;
}
/* enough with the ip dependency already. This is a trivial checksum. */
static inline uint16_t cb_checksum(const void *ptr, unsigned len)
{
uint32_t sum;
const uint8_t *addr = ptr;
sum = 0;
while(len > 0) {
sum += addr[0]<<8 | addr[1] ;
len -= 2;
addr += 2;
}
sum = (sum & 0xffff) + (sum >> 16);
sum = (sum & 0xffff) + (sum >> 16);
return (sum^0xffff);
}
static void *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;
}
first_rec = lb_first_record(head);
head->table_checksum = cb_checksum((void *)first_rec, head->table_bytes);
head->header_checksum = 0;
head->header_checksum = cb_checksum((void *)head, sizeof(*head));
printf("Wrote coreboot table at: %p, 0x%x bytes, checksum %x\n",
head, head->table_bytes, head->table_checksum);
printf("header checksum (not worth using in a kernel) 0x%x\n", head->header_checksum);
return rec + rec->size;
}
void * write_coreboot_table(void *where, void *base, uint64_t len)
{
struct lb_header *head;
struct lb_memory *m;
struct lb_memory_range *mem;
printf("Writing coreboot table at %p with mem %p len 0x%lx\n", where, base, len);
head = lb_table_init(where);
m = lb_memory(head);
mem = (void *)(&m[1]);
mem->start = pack_lb64((uint64_t) 0);
mem->size = pack_lb64((uint64_t) base-1);
mem->type = LB_MEM_RESERVED;
m->size += sizeof(*mem);
mem = (void *)(&mem[1]);
mem->start = pack_lb64((uint64_t) base);
mem->size = pack_lb64((uint64_t) len);
mem->type = LB_MEM_RAM;
m->size += sizeof(*mem);
printf("Head is %p, mem is %p, m is %p, m->size is %d, mem->size is %d\n",
head, mem, m, m->size, mem->size);
return lb_table_fini(head);
}