1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
|
#include "linuxbios_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 *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;
ptr = addr;
memmove(&buffer, ptr, 1);
sum += buffer;
if (sum > 0xFFFF)
sum -= 0xFFFF;
length -= 1;
addr = 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 *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;
ptr = addr;
memmove(&buffer, 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;
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);
}
return head;
}
int query_linuxbios(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;
}
|