1 /* Adapted from Etherboot 5.1.8 */
2 
3 #include "config.h"
4 #include "sysinclude.h"
5 #include "asm/types.h"
6 #include "asm/io.h"
7 #include "linuxbios.h"
8 #include "libopenbios/ipchecksum.h"
9 #include "libopenbios/sys_info.h"
10 
11 #ifdef CONFIG_DEBUG_BOOT
12 #define debug printk
13 #else
14 #define debug(x...)
15 #endif
16 
17 #define for_each_lbrec(head, rec) \
18 	for(rec = (struct lb_record *)(((char *)head) + sizeof(*head)); \
19 		(((char *)rec) < (((char *)head) + sizeof(*head) + head->table_bytes))  && \
20 		(rec->size >= 1) && \
21 		((((char *)rec) + rec->size) <= (((char *)head) + sizeof(*head) + head->table_bytes)); \
22 		rec = (struct lb_record *)(((char *)rec) + rec->size))
23 
convert_memmap(struct lb_memory * lbmem,struct sys_info * info)24 static void convert_memmap(struct lb_memory *lbmem, struct sys_info *info)
25 {
26     int lbcount;
27     int i;
28 
29     lbcount = lbmem->size / sizeof(struct lb_memory_range);
30     info->memrange = malloc(lbcount * sizeof(struct memrange));
31     info->n_memranges = 0;
32     for (i = 0; i < lbcount; i++) {
33 	debug("%#016llx %#016llx %d\n",
34               (long long)lbmem->map[i].start, (long long)lbmem->map[i].size,
35               (int) lbmem->map[i].type);
36 	if (lbmem->map[i].type != LB_MEM_RAM)
37 	    continue;
38 	info->memrange[info->n_memranges].base = lbmem->map[i].start;
39 	info->memrange[info->n_memranges].size = lbmem->map[i].size;
40 	info->n_memranges++;
41     }
42 }
43 
read_lbtable(struct lb_header * head,struct sys_info * info)44 static int read_lbtable(struct lb_header *head, struct sys_info *info)
45 {
46 	int retval = 0;
47 
48 	/* Read linuxbios tables... */
49 	struct lb_record *rec;
50 
51 	for_each_lbrec(head, rec) {
52 		switch(rec->tag) {
53 		case LB_TAG_MEMORY:
54 			convert_memmap((struct lb_memory *) rec, info);
55 			retval = 1;
56 			break;
57 		};
58 	}
59 	return retval;
60 }
61 
count_lb_records(void * start,unsigned long length)62 static unsigned long count_lb_records(void *start, unsigned long length)
63 {
64 	struct lb_record *rec;
65 	void *end;
66 	unsigned long count;
67 	count = 0;
68 	end = ((char *)start) + length;
69 	for(rec = start; ((void *)rec < end) &&
70 		((signed long)rec->size <=
71                  ((signed long)end - (signed long)rec));
72 		rec = (void *)(((char *)rec) + rec->size)) {
73 		count++;
74 	}
75 	return count;
76 }
77 
find_lb_table(void * start,void * end,struct lb_header ** result)78 static int find_lb_table(void *start, void *end, struct lb_header **result)
79 {
80 	unsigned char *ptr;
81 	/* For now be stupid.... */
82 	for(ptr = start; (void *)ptr < end; ptr += 16) {
83 		struct lb_header *head = (struct lb_header *)ptr;
84 		if (	(head->signature[0] != 'L') ||
85 			(head->signature[1] != 'B') ||
86 			(head->signature[2] != 'I') ||
87 			(head->signature[3] != 'O')) {
88 			continue;
89 		}
90 		if (head->header_bytes != sizeof(*head))
91 			continue;
92 		debug("Found canidate at: %p\n", head);
93 		if (ipchksum((uint16_t *)head, sizeof(*head)) != 0)
94 			continue;
95 		debug("header checksum o.k.\n");
96 		if (ipchksum((uint16_t *)(ptr + sizeof(*head)), head->table_bytes) !=
97 			head->table_checksum) {
98 			continue;
99 		}
100 		debug("table checksum o.k.\n");
101 		if (count_lb_records(ptr + sizeof(*head), head->table_bytes) !=
102 			head->table_entries) {
103 			continue;
104 		}
105 		debug("record count o.k.\n");
106 		*result = head;
107 		return 1;
108 	};
109 	return 0;
110 }
111 
collect_linuxbios_info(struct sys_info * info)112 void collect_linuxbios_info(struct sys_info *info)
113 {
114 	struct lb_header *lb_table;
115 	int found;
116 	debug("Searching for LinuxBIOS tables...\n");
117 	found = 0;
118 	if (!found) {
119 		found = find_lb_table(phys_to_virt(0x00000), phys_to_virt(0x01000), &lb_table);
120 	}
121 	if (!found) {
122 		found = find_lb_table(phys_to_virt(0xf0000), phys_to_virt(0x100000), &lb_table);
123 	}
124 	if (!found)
125 		return;
126 
127 	debug("Found LinuxBIOS table at: %p\n", lb_table);
128 	info->firmware = "LinuxBIOS";
129 	read_lbtable(lb_table, info);
130 }
131