blob: bcc51f8b5e9846c240191389e4671816ce047414 [file] [log] [blame]
#include "coreboot_tables.h"
#include "test.h"
static unsigned long ip_compute_csum(void *addr, unsigned long length)
{
uint16_t *ptr;
unsigned long sum;
unsigned long len;
unsigned long laddr;
/* compute an ip style checksum */
laddr = (unsigned long )addr;
sum = 0;
if (laddr & 1) {
uint16_t buffer;
unsigned char *tmp_ptr;
/* copy the first byte into a 2 byte buffer.
* This way automatically handles the endian question
* of which byte (low or high) the last byte goes in.
*/
buffer = 0;
tmp_ptr = addr;
memmove(&buffer, tmp_ptr, 1);
sum += buffer;
if (sum > 0xFFFF)
sum -= 0xFFFF;
length -= 1;
addr = tmp_ptr + 1;
}
len = length >> 1;
ptr = addr;
while (len--) {
sum += *(ptr++);
if (sum > 0xFFFF)
sum -= 0xFFFF;
}
addr = ptr;
if (length & 1) {
uint16_t buffer;
unsigned char *tmp_ptr;
/* copy the last byte into a 2 byte buffer.
* This way automatically handles the endian question
* of which byte (low or high) the last byte goes in.
*/
buffer = 0;
tmp_ptr = addr;
memmove(&buffer, tmp_ptr, 1);
sum += buffer;
if (sum > 0xFFFF)
sum -= 0xFFFF;
}
return (~sum) & 0xFFFF;
}
#define for_each_lbrec(head, rec) \
for (rec = (struct lb_record *)(((char *)head) + sizeof(*head)); \
(((char *)rec) < (((char *)head) + sizeof(*head) + head->table_bytes)) && \
(rec->size >= 1) && \
((((char *)rec) + rec->size) <= (((char *)head) + sizeof(*head) + head->table_bytes)); \
rec = (struct lb_record *)(((char *)rec) + rec->size))
static int count_lb_records(struct lb_header *head)
{
struct lb_record *rec;
int count;
count = 0;
for_each_lbrec(head, rec) {
count++;
}
return count;
}
static struct lb_header * __find_lb_table(unsigned long start, unsigned long end)
{
unsigned long addr;
/* For now be stupid.... */
for (addr = start; addr < end; addr += 16) {
struct lb_header *head = (struct lb_header *)addr;
struct lb_record *recs = (struct lb_record *)(addr + sizeof(*head));
if (memcmp(head->signature, "LBIO", 4) != 0)
continue;
if (head->header_bytes != sizeof(*head))
continue;
if (ip_compute_csum((unsigned char *)head, sizeof(*head)) != 0)
continue;
if (ip_compute_csum((unsigned char *)recs, head->table_bytes)
!= head->table_checksum)
continue;
if (count_lb_records(head) != head->table_entries)
continue;
return head;
}
return 0;
}
static struct lb_header * find_lb_table(void)
{
struct lb_header *head;
struct lb_record *rec;
struct lb_forward *forward;
head = 0;
if (!head) {
/* First try at address 0 */
head = __find_lb_table(0x00000, 0x1000);
}
if (!head) {
/* Then try at address 0xf0000 */
head = __find_lb_table(0xf0000, 0x100000);
}
forward = 0;
if (head) {
/* Check whether there is a forward header */
for_each_lbrec(head, rec) {
if (rec->tag == LB_TAG_FORWARD) {
forward = (struct lb_forward *)rec;
break;
}
}
}
if (forward) {
/* if there is, all valid information is in the
* referenced coreboot table
*/
head = __find_lb_table(forward->forward,
forward->forward + 0x1000);
}
return head;
}
int query_coreboot(void)
{
struct lb_header *head;
struct lb_record *rec;
struct lb_memory *mem;
struct lb_forward *forward;
int i, entries;
head = find_lb_table();
if (!head) {
return 0;
}
/* coreboot also can forward the table to the high tables area. */
rec = (struct lb_record *)(((char *)head) + sizeof(*head));
if (rec->tag == LB_TAG_FORWARD) {
forward = (struct lb_forward *)rec;
head = (struct lb_header *)(unsigned long)(forward->forward);
if (!head) { return 0; }
}
mem = 0;
for_each_lbrec(head, rec) {
if (rec->tag == LB_TAG_MEMORY) {
mem = (struct lb_memory *)rec;
break;
}
}
if (!mem) {
return 1;
}
entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]);
if (entries == 0)
return 1;
mem_info.e820_nr = 0;
for (i = 0; i < entries; i++) {
unsigned long long start;
unsigned long long size;
unsigned long type;
if (i >= E820MAX) {
break;
}
start = mem->map[i].start;
size = mem->map[i].size;
type = (mem->map[i].type == LB_MEM_RAM) ? E820_RAM : E820_RESERVED;
mem_info.e820[mem_info.e820_nr].addr = start;
mem_info.e820[mem_info.e820_nr].size = size;
mem_info.e820[mem_info.e820_nr].type = type;
mem_info.e820_nr++;
}
return 1;
}