VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/Etherboot-src/firmware/linuxbios/linuxbios.c@ 1434

Last change on this file since 1434 was 1, checked in by vboxsync, 55 years ago

import

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 9.6 KB
Line 
1#ifdef LINUXBIOS
2
3#include "etherboot.h"
4#include "dev.h"
5#include "linuxbios_tables.h"
6
7struct meminfo meminfo;
8static int lb_failsafe = 1;
9static unsigned lb_boot[MAX_BOOT_ENTRIES];
10static unsigned lb_boot_index;
11static struct cmos_entries lb_countdown;
12static struct cmos_checksum lb_checksum;
13
14#undef DEBUG_LINUXBIOS
15
16static 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}
22static 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
51static 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
104static 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
122static 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
130static 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
148static 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
183static 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
255static 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
270static 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
313void 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
348unsigned 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 */
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette