45d348f856396a0c0dd0937a1fcc6e8a73283e20
[seabios.git] / src / optionroms.c
1 // Option rom scanning code.
2 //
3 // Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2002  MandrakeSoft S.A.
5 //
6 // This file may be distributed under the terms of the GNU GPLv3 license.
7
8 #include "bregs.h" // struct bregs
9 #include "biosvar.h" // struct ipl_entry_s
10 #include "util.h" // dprintf
11
12 // $PnP string with special alignment in romlayout.S
13 extern char pnp_string[];
14
15 struct rom_header {
16     u16 signature;
17     u8 size;
18     u8 initVector[4];
19     u8 reserved[17];
20     u16 pcioffset;
21     u16 pnpoffset;
22 } PACKED;
23
24 struct pci_data {
25     u32 signature;
26     u16 vendor;
27     u16 device;
28     u16 vitaldata;
29     u16 dlen;
30     u8 drevision;
31     u8 class_lo;
32     u16 class_hi;
33     u16 ilen;
34     u16 irevision;
35     u8 type;
36     u8 indicator;
37     u16 reserved;
38 } PACKED;
39
40 struct pnp_data {
41     u32 signature;
42     u8 revision;
43     u8 len;
44     u16 nextoffset;
45     u8 reserved_08;
46     u8 checksum;
47     u32 devid;
48     u16 manufacturer;
49     u16 productname;
50     u8 type_lo;
51     u16 type_hi;
52     u8 dev_flags;
53     u16 bcv;
54     u16 dv;
55     u16 bev;
56     u16 reserved_1c;
57     u16 staticresource;
58 } PACKED;
59
60 // Execute a given option rom.
61 static void
62 callrom(u16 seg, u16 offset)
63 {
64     dprintf(1, "Running option rom at %x:%x\n", seg, offset);
65
66     struct bregs br;
67     memset(&br, 0, sizeof(br));
68     // XXX - should set br.ax to PCI Bus/DevFn
69     br.bx = 0xffff;
70     br.dx = 0xffff;
71     br.es = SEG_BIOS;
72     br.di = (u32)pnp_string - BUILD_BIOS_ADDR;
73     br.cs = seg;
74     br.ip = offset;
75     call16(&br);
76
77     debug_serial_setup();
78
79     if (GET_BDA(ebda_seg) != SEG_EBDA)
80         BX_PANIC("Option rom at %x:%x attempted to move ebda from %x to %x\n"
81                  , seg, offset, SEG_EBDA, GET_BDA(ebda_seg));
82 }
83
84 #define ebda ((struct extended_bios_data_area_s *)MAKE_FARPTR(SEG_EBDA, 0))
85
86 // Find and run any "option roms" found in the given address range.
87 static void
88 rom_scan(u32 start, u32 end)
89 {
90     if (! CONFIG_OPTIONROMS)
91         return;
92
93     u8 *p = (u8*)start;
94     for (; p < (u8*)end; p += 2048) {
95         struct rom_header *rom = (struct rom_header *)p;
96         if (rom->signature != 0xaa55)
97             continue;
98         u32 len = rom->size * 512;
99         u8 sum = checksum(p, len);
100         if (sum != 0) {
101             dprintf(1, "Found option rom with bad checksum:"
102                     " loc=%p len=%d sum=%x\n"
103                     , rom, len, sum);
104             continue;
105         }
106         p = (u8*)(((u32)p + len) / 2048 * 2048);
107         callrom(FARPTR_TO_SEG(rom), FARPTR_TO_OFFSET(rom->initVector));
108
109         // Look at the ROM's PnP Expansion header.  Properly, we're supposed
110         // to init all the ROMs and then go back and build an IPL table of
111         // all the bootable devices, but we can get away with one pass.
112         struct pnp_data *pnp = (struct pnp_data *)((u8*)rom + rom->pnpoffset);
113         if (pnp->signature != *(u32*)pnp_string)
114             continue;
115         u16 entry = pnp->bev;
116         if (!entry)
117             continue;
118         // Found a device that thinks it can boot the system.  Record
119         // its BEV and product name string.
120
121         if (! CONFIG_BOOT)
122             continue;
123
124         if (ebda->ipl.count >= ARRAY_SIZE(ebda->ipl.table))
125             continue;
126
127         struct ipl_entry_s *ip = &ebda->ipl.table[ebda->ipl.count];
128         ip->type = IPL_TYPE_BEV;
129         ip->vector = (FARPTR_TO_SEG(rom) << 16) | entry;
130
131         u16 desc = pnp->productname;
132         if (desc)
133             ip->description = (u32)MAKE_FARPTR(FARPTR_TO_SEG(rom), desc);
134
135         ebda->ipl.count++;
136     }
137 }
138
139 // Call into vga code to turn on console.
140 void
141 vga_setup()
142 {
143     dprintf(1, "Scan for VGA option rom\n");
144     rom_scan(0xc0000, 0xc8000);
145
146     dprintf(1, "Turning on vga console\n");
147     struct bregs br;
148     memset(&br, 0, sizeof(br));
149     br.ax = 0x0003;
150     call16_int(0x10, &br);
151
152     // Write to screen.
153     printf("Starting SeaBIOS\n\n");
154 }
155
156 void
157 optionrom_setup()
158 {
159     dprintf(1, "Scan for option roms\n");
160     rom_scan(0xc8000, 0xf0000);
161 }