| /* |
| * 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); |
| } |