Basic support for PCI BIOS.
[seabios.git] / src / pcibios.c
1 // Low level ATA disk access
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 "types.h" // u32
9 #include "util.h" // handle_1ab1
10 #include "pci.h" // pci_config_readl
11
12
13 /****************************************************************
14  * PIR table
15  ****************************************************************/
16
17 struct pir {
18     u32 signature;
19     u16 version;
20     u16 size;
21     u8 router_bus;
22     u8 router_devfunc;
23     u16 exclusive_irqs;
24     u32 compatible_devid;
25     u32 miniport_data;
26     u8 reserved[11];
27     u8 checksum;
28 } PACKED;
29
30 struct link_info {
31     u8 link;
32     u16 bitmap;
33 } PACKED;
34
35 struct pir_slot {
36     u8 bus;
37     u8 dev;
38     struct link_info links[4];
39     u8 slot_nr;
40     u8 reserved;
41 } PACKED;
42
43 struct pir_table {
44     struct pir pir;
45     struct pir_slot slots[6];
46 } PACKED PIR_TABLE VISIBLE16 __attribute__((aligned(16))) = {
47 #if CONFIG_PCIBIOS
48     .pir = {
49         .signature = 0x52495024, // "$PIR"
50         .version = 0x0100,
51         .size = sizeof(struct pir_table),
52         .router_devfunc = 0x08,
53         .compatible_devid = 0x70008086,
54         .checksum = 0x07, // XXX - should auto calculate
55     },
56     .slots = {
57         {
58             // first slot entry PCI-to-ISA (embedded)
59             .dev = 1<<3,
60             .links = {
61                 {.link = 0x60, .bitmap = 0xdef8}, // INTA#
62                 {.link = 0x61, .bitmap = 0xdef8}, // INTB#
63                 {.link = 0x62, .bitmap = 0xdef8}, // INTC#
64                 {.link = 0x63, .bitmap = 0xdef8}, // INTD#
65             },
66             .slot_nr = 0, // embedded
67         }, {
68             // second slot entry: 1st PCI slot
69             .dev = 2<<3,
70             .links = {
71                 {.link = 0x61, .bitmap = 0xdef8}, // INTA#
72                 {.link = 0x62, .bitmap = 0xdef8}, // INTB#
73                 {.link = 0x63, .bitmap = 0xdef8}, // INTC#
74                 {.link = 0x60, .bitmap = 0xdef8}, // INTD#
75             },
76             .slot_nr = 1,
77         }, {
78             // third slot entry: 2nd PCI slot
79             .dev = 3<<3,
80             .links = {
81                 {.link = 0x62, .bitmap = 0xdef8}, // INTA#
82                 {.link = 0x63, .bitmap = 0xdef8}, // INTB#
83                 {.link = 0x60, .bitmap = 0xdef8}, // INTC#
84                 {.link = 0x61, .bitmap = 0xdef8}, // INTD#
85             },
86             .slot_nr = 2,
87         }, {
88             // 4th slot entry: 3rd PCI slot
89             .dev = 4<<3,
90             .links = {
91                 {.link = 0x63, .bitmap = 0xdef8}, // INTA#
92                 {.link = 0x60, .bitmap = 0xdef8}, // INTB#
93                 {.link = 0x61, .bitmap = 0xdef8}, // INTC#
94                 {.link = 0x62, .bitmap = 0xdef8}, // INTD#
95             },
96             .slot_nr = 3,
97         }, {
98             // 5th slot entry: 4rd PCI slot
99             .dev = 5<<3,
100             .links = {
101                 {.link = 0x60, .bitmap = 0xdef8}, // INTA#
102                 {.link = 0x61, .bitmap = 0xdef8}, // INTB#
103                 {.link = 0x62, .bitmap = 0xdef8}, // INTC#
104                 {.link = 0x63, .bitmap = 0xdef8}, // INTD#
105             },
106             .slot_nr = 4,
107         }, {
108             // 6th slot entry: 5rd PCI slot
109             .dev = 6<<3,
110             .links = {
111                 {.link = 0x61, .bitmap = 0xdef8}, // INTA#
112                 {.link = 0x62, .bitmap = 0xdef8}, // INTB#
113                 {.link = 0x63, .bitmap = 0xdef8}, // INTC#
114                 {.link = 0x60, .bitmap = 0xdef8}, // INTD#
115             },
116             .slot_nr = 5,
117         },
118     }
119 #endif // CONFIG_PCIBIOS
120 };
121
122
123 /****************************************************************
124  * Helper functions
125  ****************************************************************/
126
127 #define RET_FUNC_NOT_SUPPORTED 0x81
128 #define RET_BAD_VENDOR_ID      0x83
129 #define RET_DEVICE_NOT_FOUND   0x86
130 #define RET_BUFFER_TOO_SMALL   0x89
131
132 // installation check
133 static void
134 handle_1ab101(struct bregs *regs)
135 {
136     regs->ax = 0x0001;
137     regs->bx = 0x0210;
138     regs->cx = 0;
139     regs->edx = 0x20494350; // "PCI "
140     // XXX - bochs bios code sets edi to point to 32bit code - but no
141     // reference to this in spec.
142     set_cf(regs, 0);
143 }
144
145 // find pci device
146 static void
147 handle_1ab102(struct bregs *regs)
148 {
149     u32 dev = (regs->cx << 16) | regs->dx;
150     u16 index = regs->si;
151     int i;
152     for (i=0; i<0x100; i++) {
153         PCIDevice d = {0, i};
154         u32 v = pci_config_readl(&d, 0);
155         if (v != dev)
156             continue;
157         if (index) {
158             index--;
159             continue;
160         }
161         // Found it.
162         regs->bx = i;
163         set_code_success(regs);
164         return;
165     }
166     set_code_fail(regs, RET_DEVICE_NOT_FOUND);
167 }
168
169 // find class code
170 static void
171 handle_1ab103(struct bregs *regs)
172 {
173     u32 code = regs->ecx << 8;
174     u16 index = regs->si;
175     int i;
176     for (i=0; i<0x100; i++) {
177         PCIDevice d = {0, i};
178         u32 v = pci_config_readl(&d, 0x08);
179         if (v != code)
180             continue;
181         if (index) {
182             index--;
183             continue;
184         }
185         // Found it.
186         regs->bx = i;
187         set_code_success(regs);
188         return;
189     }
190     set_code_fail(regs, RET_DEVICE_NOT_FOUND);
191 }
192
193 // read configuration byte
194 static void
195 handle_1ab108(struct bregs *regs)
196 {
197     PCIDevice d = {regs->bh, regs->bl};
198     regs->cl = pci_config_readb(&d, regs->di);
199     set_code_success(regs);
200 }
201
202 // read configuration word
203 static void
204 handle_1ab109(struct bregs *regs)
205 {
206     PCIDevice d = {regs->bh, regs->bl};
207     regs->cx = pci_config_readw(&d, regs->di);
208     set_code_success(regs);
209 }
210
211 // read configuration dword
212 static void
213 handle_1ab10a(struct bregs *regs)
214 {
215     PCIDevice d = {regs->bh, regs->bl};
216     regs->ecx = pci_config_readl(&d, regs->di);
217     set_code_success(regs);
218 }
219
220 // write configuration byte
221 static void
222 handle_1ab10b(struct bregs *regs)
223 {
224     PCIDevice d = {regs->bh, regs->bl};
225     pci_config_writeb(&d, regs->di, regs->cl);
226     set_code_success(regs);
227 }
228
229 // write configuration word
230 static void
231 handle_1ab10c(struct bregs *regs)
232 {
233     PCIDevice d = {regs->bh, regs->bl};
234     pci_config_writew(&d, regs->di, regs->cx);
235     set_code_success(regs);
236 }
237
238 // write configuration dword
239 static void
240 handle_1ab10d(struct bregs *regs)
241 {
242     PCIDevice d = {regs->bh, regs->bl};
243     pci_config_writel(&d, regs->di, regs->ecx);
244     set_code_success(regs);
245 }
246
247 // get irq routing options
248 static void
249 handle_1ab10e(struct bregs *regs)
250 {
251     // Validate and update size.
252     u16 size = GET_FARVAR(regs->es, *(u16*)(regs->di+0));
253     u32 pirsize = sizeof(PIR_TABLE.slots);
254     SET_FARVAR(regs->es, *(u16*)(regs->di+0), pirsize);
255     if (size < pirsize) {
256         set_code_fail(regs, RET_BUFFER_TOO_SMALL);
257         return;
258     }
259
260     // Get dest buffer.
261     u8 *d = (u8*)(GET_FARVAR(regs->es, *(u16*)(regs->di+2)) + 0);
262     u16 destseg = GET_FARVAR(regs->es, *(u16*)(regs->di+4));
263
264     // Memcpy pir table slots to dest buffer.
265     u8 *p = (u8*)PIR_TABLE.slots;
266     u8 *end = p + pirsize;
267     for (; p<end; p++, d++) {
268         u8 c = GET_VAR(CS, *p);
269         SET_FARVAR(destseg, *d, c);
270     }
271
272     // XXX - bochs bios sets bx to (1 << 9) | (1 << 11)
273     regs->bx = GET_VAR(CS, PIR_TABLE.pir.exclusive_irqs);
274     set_code_success(regs);
275 }
276
277 static void
278 handle_1ab1XX(struct bregs *regs)
279 {
280     set_code_fail(regs, RET_FUNC_NOT_SUPPORTED);
281 }
282
283 #define PCI_FIXED_HOST_BRIDGE 0x12378086 // i440FX PCI bridge
284
285 void
286 handle_1ab1(struct bregs *regs)
287 {
288     //debug_stub(regs);
289
290     if (! CONFIG_PCIBIOS) {
291         set_fail(regs);
292         return;
293     }
294
295     outl(0x80000000, 0x0cf8);
296     u32 v = inl(0x0cfc);
297     if (
298 #ifdef PCI_FIXED_HOST_BRIDGE
299         v != PCI_FIXED_HOST_BRIDGE
300 #else
301         v == 0xffffffff
302 #endif
303         ) {
304         // Device not present
305         set_code_fail(regs, 0xff);
306         return;
307     }
308
309     switch (regs->al) {
310     case 0x01: handle_1ab101(regs); break;
311     case 0x02: handle_1ab102(regs); break;
312     case 0x03: handle_1ab103(regs); break;
313     case 0x08: handle_1ab108(regs); break;
314     case 0x09: handle_1ab109(regs); break;
315     case 0x0a: handle_1ab10a(regs); break;
316     case 0x0b: handle_1ab10b(regs); break;
317     case 0x0c: handle_1ab10c(regs); break;
318     case 0x0d: handle_1ab10d(regs); break;
319     case 0x0e: handle_1ab10e(regs); break;
320     default:   handle_1ab1XX(regs); break;
321     }
322 }