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
|
#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;
int i, entries;
head = find_lb_table();
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;
}
|