| /* linuxbios.c - MemTest-86 Version 3.3 |
| * |
| * Released under version 2 of the Gnu Public License. |
| * By Eric Biederman |
| */ |
| |
| #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; |
| |
| /* First try at address 0 */ |
| head = __find_lb_table(0x00000, 0x1000); |
| |
| if (!head) { |
| /* Then try at address 0xf0000 */ |
| head = __find_lb_table(0xf0000, 0x100000); |
| } |
| |
| if (head) { |
| struct lb_record *rec; |
| |
| /* Check whether there is a forward header */ |
| for_each_lbrec(head, rec) { |
| if (rec->tag == LB_TAG_FORWARD) { |
| struct lb_hwrpb *forward = |
| (struct lb_hwrpb *)rec; |
| head = __find_lb_table |
| ((unsigned)forward->hwrpb, |
| (unsigned)forward->hwrpb + 0x100); |
| break; |
| } |
| } |
| } |
| 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; |
| } |
| |