1 | #ifdef LINUXBIOS
|
---|
2 |
|
---|
3 | #include "etherboot.h"
|
---|
4 | #include "dev.h"
|
---|
5 | #include "linuxbios_tables.h"
|
---|
6 |
|
---|
7 | struct meminfo meminfo;
|
---|
8 | static int lb_failsafe = 1;
|
---|
9 | static unsigned lb_boot[MAX_BOOT_ENTRIES];
|
---|
10 | static unsigned lb_boot_index;
|
---|
11 | static struct cmos_entries lb_countdown;
|
---|
12 | static struct cmos_checksum lb_checksum;
|
---|
13 |
|
---|
14 | #undef DEBUG_LINUXBIOS
|
---|
15 |
|
---|
16 | static void set_base_mem_k(struct meminfo *info, unsigned mem_k)
|
---|
17 | {
|
---|
18 | if ((mem_k <= 640) && (info->basememsize <= mem_k)) {
|
---|
19 | info->basememsize = mem_k;
|
---|
20 | }
|
---|
21 | }
|
---|
22 | static void set_high_mem_k(struct meminfo *info, unsigned mem_k)
|
---|
23 | {
|
---|
24 | /* Shave off a megabyte before playing */
|
---|
25 | if (mem_k < 1024) {
|
---|
26 | return;
|
---|
27 | }
|
---|
28 | mem_k -= 1024;
|
---|
29 | if (info->memsize <= mem_k) {
|
---|
30 | info->memsize = mem_k;
|
---|
31 | }
|
---|
32 | }
|
---|
33 |
|
---|
34 | #define for_each_lbrec(head, rec) \
|
---|
35 | for(rec = (struct lb_record *)(((char *)head) + sizeof(*head)); \
|
---|
36 | (((char *)rec) < (((char *)head) + sizeof(*head) + head->table_bytes)) && \
|
---|
37 | (rec->size >= 1) && \
|
---|
38 | ((((char *)rec) + rec->size) <= (((char *)head) + sizeof(*head) + head->table_bytes)); \
|
---|
39 | rec = (struct lb_record *)(((char *)rec) + rec->size))
|
---|
40 |
|
---|
41 |
|
---|
42 | #define for_each_crec(tbl, rec) \
|
---|
43 | for(rec = (struct lb_record *)(((char *)tbl) + tbl->header_length); \
|
---|
44 | (((char *)rec) < (((char *)tbl) + tbl->size)) && \
|
---|
45 | (rec->size >= 1) && \
|
---|
46 | ((((char *)rec) + rec->size) <= (((char *)tbl) + tbl->size)); \
|
---|
47 | rec = (struct lb_record *)(((char *)rec) + rec->size))
|
---|
48 |
|
---|
49 |
|
---|
50 |
|
---|
51 | static void read_lb_memory(
|
---|
52 | struct meminfo *info, struct lb_memory *mem)
|
---|
53 | {
|
---|
54 | int i;
|
---|
55 | int entries;
|
---|
56 | entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]);
|
---|
57 | for(i = 0; (i < entries); i++) {
|
---|
58 | if (info->map_count < E820MAX) {
|
---|
59 | info->map[info->map_count].addr = mem->map[i].start;
|
---|
60 | info->map[info->map_count].size = mem->map[i].size;
|
---|
61 | info->map[info->map_count].type = mem->map[i].type;
|
---|
62 | info->map_count++;
|
---|
63 | }
|
---|
64 | switch(mem->map[i].type) {
|
---|
65 | case LB_MEM_RAM:
|
---|
66 | {
|
---|
67 | unsigned long long end;
|
---|
68 | unsigned long mem_k;
|
---|
69 | end = mem->map[i].start + mem->map[i].size;
|
---|
70 | #if defined(DEBUG_LINUXBIOS)
|
---|
71 | printf("lb: %X%X - %X%X (ram)\n",
|
---|
72 | (unsigned long)(mem->map[i].start >>32),
|
---|
73 | (unsigned long)(mem->map[i].start & 0xFFFFFFFF),
|
---|
74 | (unsigned long)(end >> 32),
|
---|
75 | (unsigned long)(end & 0xFFFFFFFF));
|
---|
76 | #endif /* DEBUG_LINUXBIOS */
|
---|
77 | end >>= 10;
|
---|
78 | mem_k = end;
|
---|
79 | if (end & 0xFFFFFFFF00000000ULL) {
|
---|
80 | mem_k = 0xFFFFFFFF;
|
---|
81 | }
|
---|
82 | set_base_mem_k(info, mem_k);
|
---|
83 | set_high_mem_k(info, mem_k);
|
---|
84 | break;
|
---|
85 | }
|
---|
86 | case LB_MEM_RESERVED:
|
---|
87 | default:
|
---|
88 | #if defined(DEBUG_LINUXBIOS)
|
---|
89 | {
|
---|
90 | unsigned long long end;
|
---|
91 | end = mem->map[i].start + mem->map[i].size;
|
---|
92 | printf("lb: %X%X - %X%X (reserved)\n",
|
---|
93 | (unsigned long)(mem->map[i].start >>32),
|
---|
94 | (unsigned long)(mem->map[i].start & 0xFFFFFFFF),
|
---|
95 | (unsigned long)(end >> 32),
|
---|
96 | (unsigned long)(end & 0xFFFFFFFF));
|
---|
97 | }
|
---|
98 | #endif /* DEBUG_LINUXBIOS */
|
---|
99 | break;
|
---|
100 | }
|
---|
101 | }
|
---|
102 | }
|
---|
103 |
|
---|
104 | static unsigned cmos_read(unsigned offset, unsigned int size)
|
---|
105 | {
|
---|
106 | unsigned addr, old_addr;
|
---|
107 | unsigned value;
|
---|
108 |
|
---|
109 | addr = offset/8;
|
---|
110 |
|
---|
111 | old_addr = inb(0x70);
|
---|
112 | outb(addr | (old_addr &0x80), 0x70);
|
---|
113 | value = inb(0x71);
|
---|
114 | outb(old_addr, 0x70);
|
---|
115 |
|
---|
116 | value >>= offset & 0x7;
|
---|
117 | value &= ((1 << size) - 1);
|
---|
118 |
|
---|
119 | return value;
|
---|
120 | }
|
---|
121 |
|
---|
122 | static unsigned cmos_read_checksum(void)
|
---|
123 | {
|
---|
124 | unsigned sum =
|
---|
125 | (cmos_read(lb_checksum.location, 8) << 8) |
|
---|
126 | cmos_read(lb_checksum.location +8, 8);
|
---|
127 | return sum & 0xffff;
|
---|
128 | }
|
---|
129 |
|
---|
130 | static int cmos_valid(void)
|
---|
131 | {
|
---|
132 | unsigned i;
|
---|
133 | unsigned sum, old_sum;
|
---|
134 | sum = 0;
|
---|
135 | if ((lb_checksum.tag != LB_TAG_OPTION_CHECKSUM) ||
|
---|
136 | (lb_checksum.type != CHECKSUM_PCBIOS) ||
|
---|
137 | (lb_checksum.size != sizeof(lb_checksum))) {
|
---|
138 | return 0;
|
---|
139 | }
|
---|
140 | for(i = lb_checksum.range_start; i <= lb_checksum.range_end; i+= 8) {
|
---|
141 | sum += cmos_read(i, 8);
|
---|
142 | }
|
---|
143 | sum = (~sum)&0x0ffff;
|
---|
144 | old_sum = cmos_read_checksum();
|
---|
145 | return sum == old_sum;
|
---|
146 | }
|
---|
147 |
|
---|
148 | static void cmos_write(unsigned offset, unsigned int size, unsigned setting)
|
---|
149 | {
|
---|
150 | unsigned addr, old_addr;
|
---|
151 | unsigned value, mask, shift;
|
---|
152 | unsigned sum;
|
---|
153 |
|
---|
154 | addr = offset/8;
|
---|
155 |
|
---|
156 | shift = offset & 0x7;
|
---|
157 | mask = ((1 << size) - 1) << shift;
|
---|
158 | setting = (setting << shift) & mask;
|
---|
159 |
|
---|
160 | old_addr = inb(0x70);
|
---|
161 | sum = cmos_read_checksum();
|
---|
162 | sum = (~sum) & 0xffff;
|
---|
163 |
|
---|
164 | outb(addr | (old_addr &0x80), 0x70);
|
---|
165 | value = inb(0x71);
|
---|
166 | sum -= value;
|
---|
167 | value &= ~mask;
|
---|
168 | value |= setting;
|
---|
169 | sum += value;
|
---|
170 | outb(value, 0x71);
|
---|
171 |
|
---|
172 | sum = (~sum) & 0x0ffff;
|
---|
173 | outb((lb_checksum.location/8) | (old_addr & 0x80), 0x70);
|
---|
174 | outb((sum >> 8) & 0xff, 0x71);
|
---|
175 | outb(((lb_checksum.location +8)/8) | (old_addr & 0x80), 0x70);
|
---|
176 | outb(sum & 0xff, 0x71);
|
---|
177 |
|
---|
178 | outb(old_addr, 0x70);
|
---|
179 |
|
---|
180 | return;
|
---|
181 | }
|
---|
182 |
|
---|
183 | static void read_linuxbios_values(struct meminfo *info,
|
---|
184 | struct lb_header *head)
|
---|
185 | {
|
---|
186 | /* Read linuxbios tables... */
|
---|
187 | struct lb_record *rec;
|
---|
188 | memset(lb_boot, 0, sizeof(lb_boot));
|
---|
189 | for_each_lbrec(head, rec) {
|
---|
190 | switch(rec->tag) {
|
---|
191 | case LB_TAG_MEMORY:
|
---|
192 | {
|
---|
193 | struct lb_memory *mem;
|
---|
194 | mem = (struct lb_memory *) rec;
|
---|
195 | read_lb_memory(info, mem);
|
---|
196 | break;
|
---|
197 | }
|
---|
198 | case LB_TAG_CMOS_OPTION_TABLE:
|
---|
199 | {
|
---|
200 | struct cmos_option_table *tbl;
|
---|
201 | struct lb_record *crec;
|
---|
202 | struct cmos_entries *entry;
|
---|
203 | tbl = (struct cmos_option_table *)rec;
|
---|
204 | for_each_crec(tbl, crec) {
|
---|
205 | /* Pick off the checksum entry and keep it */
|
---|
206 | if (crec->tag == LB_TAG_OPTION_CHECKSUM) {
|
---|
207 | memcpy(&lb_checksum, crec, sizeof(lb_checksum));
|
---|
208 | continue;
|
---|
209 | }
|
---|
210 | if (crec->tag != LB_TAG_OPTION)
|
---|
211 | continue;
|
---|
212 | entry = (struct cmos_entries *)crec;
|
---|
213 | if ((entry->bit < 112) || (entry->bit > 1020))
|
---|
214 | continue;
|
---|
215 | /* See if LinuxBIOS came up in fallback or normal mode */
|
---|
216 | if (memcmp(entry->name, "last_boot", 10) == 0) {
|
---|
217 | lb_failsafe = cmos_read(entry->bit, entry->length) == 0;
|
---|
218 | continue;
|
---|
219 | }
|
---|
220 | /* Find where the boot countdown is */
|
---|
221 | if (memcmp(entry->name, "boot_countdown", 15) == 0) {
|
---|
222 | lb_countdown = *entry;
|
---|
223 | continue;
|
---|
224 | }
|
---|
225 | /* Find the default boot index */
|
---|
226 | if (memcmp(entry->name, "boot_index", 11) == 0) {
|
---|
227 | lb_boot_index = cmos_read(entry->bit, entry->length);
|
---|
228 | continue;
|
---|
229 | }
|
---|
230 | /* Now filter for the boot order options */
|
---|
231 | if (entry->length != 4)
|
---|
232 | continue;
|
---|
233 | if (entry->config != 'e')
|
---|
234 | continue;
|
---|
235 | if (memcmp(entry->name, "boot_first", 11) == 0) {
|
---|
236 | lb_boot[0] = cmos_read(entry->bit, entry->length);
|
---|
237 | }
|
---|
238 | else if (memcmp(entry->name, "boot_second", 12) == 0) {
|
---|
239 | lb_boot[1] = cmos_read(entry->bit, entry->length);
|
---|
240 | }
|
---|
241 | else if (memcmp(entry->name, "boot_third", 11) == 0) {
|
---|
242 | lb_boot[2] = cmos_read(entry->bit, entry->length);
|
---|
243 | }
|
---|
244 | }
|
---|
245 | break;
|
---|
246 | }
|
---|
247 | default:
|
---|
248 | break;
|
---|
249 | };
|
---|
250 | }
|
---|
251 | }
|
---|
252 |
|
---|
253 |
|
---|
254 |
|
---|
255 | static unsigned long count_lb_records(void *start, unsigned long length)
|
---|
256 | {
|
---|
257 | struct lb_record *rec;
|
---|
258 | void *end;
|
---|
259 | unsigned long count;
|
---|
260 | count = 0;
|
---|
261 | end = ((char *)start) + length;
|
---|
262 | for(rec = start; ((void *)rec < end) &&
|
---|
263 | ((signed long)rec->size <= (end - (void *)rec));
|
---|
264 | rec = (void *)(((char *)rec) + rec->size)) {
|
---|
265 | count++;
|
---|
266 | }
|
---|
267 | return count;
|
---|
268 | }
|
---|
269 |
|
---|
270 | static int find_lb_table(void *start, void *end, struct lb_header **result)
|
---|
271 | {
|
---|
272 | unsigned char *ptr;
|
---|
273 |
|
---|
274 | /* For now be stupid.... */
|
---|
275 | for(ptr = start; virt_to_phys(ptr) < virt_to_phys(end); ptr += 16) {
|
---|
276 | struct lb_header *head = (struct lb_header *)ptr;
|
---|
277 | if ( (head->signature[0] != 'L') ||
|
---|
278 | (head->signature[1] != 'B') ||
|
---|
279 | (head->signature[2] != 'I') ||
|
---|
280 | (head->signature[3] != 'O')) {
|
---|
281 | continue;
|
---|
282 | }
|
---|
283 | if (head->header_bytes != sizeof(*head))
|
---|
284 | continue;
|
---|
285 | #if defined(DEBUG_LINUXBIOS)
|
---|
286 | printf("Found canidate at: %X\n", virt_to_phys(head));
|
---|
287 | #endif
|
---|
288 | if (ipchksum((uint16_t *)head, sizeof(*head)) != 0)
|
---|
289 | continue;
|
---|
290 | #if defined(DEBUG_LINUXBIOS)
|
---|
291 | printf("header checksum o.k.\n");
|
---|
292 | #endif
|
---|
293 | if (ipchksum((uint16_t *)(ptr + sizeof(*head)), head->table_bytes) !=
|
---|
294 | head->table_checksum) {
|
---|
295 | continue;
|
---|
296 | }
|
---|
297 | #if defined(DEBUG_LINUXBIOS)
|
---|
298 | printf("table checksum o.k.\n");
|
---|
299 | #endif
|
---|
300 | if (count_lb_records(ptr + sizeof(*head), head->table_bytes) !=
|
---|
301 | head->table_entries) {
|
---|
302 | continue;
|
---|
303 | }
|
---|
304 | #if defined(DEBUG_LINUXBIOS)
|
---|
305 | printf("record count o.k.\n");
|
---|
306 | #endif
|
---|
307 | *result = head;
|
---|
308 | return 1;
|
---|
309 | };
|
---|
310 | return 0;
|
---|
311 | }
|
---|
312 |
|
---|
313 | void get_memsizes(void)
|
---|
314 | {
|
---|
315 | struct lb_header *lb_table;
|
---|
316 | int found;
|
---|
317 | #if defined(DEBUG_LINUXBIOS)
|
---|
318 | printf("\nSearching for linuxbios tables...\n");
|
---|
319 | #endif /* DEBUG_LINUXBIOS */
|
---|
320 | found = 0;
|
---|
321 | meminfo.basememsize = 0;
|
---|
322 | meminfo.memsize = 0;
|
---|
323 | meminfo.map_count = 0;
|
---|
324 | /* This code is specific to linuxBIOS but could
|
---|
325 | * concievably be extended to work under a normal bios.
|
---|
326 | * but size is important...
|
---|
327 | */
|
---|
328 | if (!found) {
|
---|
329 | found = find_lb_table(phys_to_virt(0x00000), phys_to_virt(0x01000), &lb_table);
|
---|
330 | }
|
---|
331 | if (!found) {
|
---|
332 | found = find_lb_table(phys_to_virt(0xf0000), phys_to_virt(0x100000), &lb_table);
|
---|
333 | }
|
---|
334 | if (found) {
|
---|
335 | #if defined (DEBUG_LINUXBIOS)
|
---|
336 | printf("Found LinuxBIOS table at: %X\n", virt_to_phys(lb_table));
|
---|
337 | #endif
|
---|
338 | read_linuxbios_values(&meminfo, lb_table);
|
---|
339 | }
|
---|
340 |
|
---|
341 | #if defined(DEBUG_LINUXBIOS)
|
---|
342 | printf("base_mem_k = %d high_mem_k = %d\n",
|
---|
343 | meminfo.basememsize, meminfo.memsize);
|
---|
344 | #endif /* DEBUG_LINUXBIOS */
|
---|
345 |
|
---|
346 | }
|
---|
347 |
|
---|
348 | unsigned long get_boot_order(unsigned long order, unsigned *index)
|
---|
349 | {
|
---|
350 | static int again;
|
---|
351 | static int checksum_valid;
|
---|
352 | static unsigned boot_count;
|
---|
353 | int i;
|
---|
354 |
|
---|
355 | if (!lb_failsafe && !again) {
|
---|
356 | /* Decrement the boot countdown the first time through */
|
---|
357 | checksum_valid = cmos_valid();
|
---|
358 | boot_count = cmos_read(lb_countdown.bit, lb_countdown.length);
|
---|
359 | if (boot_count > 0) {
|
---|
360 | cmos_write(lb_countdown.bit, lb_countdown.length, boot_count -1);
|
---|
361 | }
|
---|
362 | again = 1;
|
---|
363 | }
|
---|
364 | if (lb_failsafe || !checksum_valid) {
|
---|
365 | /* When LinuxBIOS is in failsafe mode, or there is an
|
---|
366 | * invalid cmos checksum ignore all cmos options
|
---|
367 | */
|
---|
368 | return order;
|
---|
369 | }
|
---|
370 | for(i = 0; i < MAX_BOOT_ENTRIES; i++) {
|
---|
371 | unsigned long boot;
|
---|
372 | boot = order >> (i*BOOT_BITS) & BOOT_MASK;
|
---|
373 | boot = lb_boot[i] & BOOT_TYPE_MASK;
|
---|
374 | if (boot >= BOOT_NOTHING) {
|
---|
375 | boot = BOOT_NOTHING;
|
---|
376 | }
|
---|
377 | if (boot_count == 0) {
|
---|
378 | boot |= BOOT_FAILSAFE;
|
---|
379 | }
|
---|
380 | order &= ~(BOOT_MASK << (i * BOOT_BITS));
|
---|
381 | order |= (boot << (i*BOOT_BITS));
|
---|
382 | }
|
---|
383 | *index = lb_boot_index;
|
---|
384 | return order;
|
---|
385 | }
|
---|
386 | #endif /* LINUXBIOS */
|
---|