6fb7d9e180cca003b138e8c3b638c11c4d911f58
[seabios.git] / src / pcibios.c
1 // PCI BIOS (int 1a/b1) calls
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 LGPLv3 license.
7
8 #include "types.h" // u32
9 #include "util.h" // handle_1ab1
10 #include "pci.h" // pci_config_readl
11 #include "bregs.h" // struct bregs
12 #include "biosvar.h" // GET_EBDA
13 #include "pci_regs.h" // PCI_VENDOR_ID
14
15 #define RET_FUNC_NOT_SUPPORTED 0x81
16 #define RET_BAD_VENDOR_ID      0x83
17 #define RET_DEVICE_NOT_FOUND   0x86
18 #define RET_BUFFER_TOO_SMALL   0x89
19
20 // installation check
21 static void
22 handle_1ab101(struct bregs *regs)
23 {
24     // Find max bus.
25     int bdf, max;
26     foreachpci(bdf, max) {
27     }
28
29     regs->al = 0x01; // Flags - "Config Mechanism #1" supported.
30     regs->bx = 0x0210; // PCI version 2.10
31     regs->cl = pci_bdf_to_bus(max - 1);
32     regs->edx = 0x20494350; // "PCI "
33     // XXX - bochs bios code sets edi to point to 32bit code - but no
34     // reference to this in spec.
35     set_code_success(regs);
36 }
37
38 // find pci device
39 static void
40 handle_1ab102(struct bregs *regs)
41 {
42     u32 id = (regs->cx << 16) | regs->dx;
43     int count = regs->si;
44     int bdf, max;
45     foreachpci(bdf, max) {
46         u32 v = pci_config_readl(bdf, PCI_VENDOR_ID);
47         if (v != id)
48             continue;
49         if (count--)
50             continue;
51         regs->bx = bdf;
52         set_code_success(regs);
53         return;
54     }
55     set_code_invalid(regs, RET_DEVICE_NOT_FOUND);
56 }
57
58 // find class code
59 static void
60 handle_1ab103(struct bregs *regs)
61 {
62     int count = regs->si;
63     u32 classprog = regs->ecx;
64     int bdf, max;
65     foreachpci(bdf, max) {
66         u32 v = pci_config_readl(bdf, PCI_CLASS_REVISION);
67         if ((v>>8) != classprog)
68             continue;
69         if (count--)
70             continue;
71         regs->bx = bdf;
72         set_code_success(regs);
73         return;
74     }
75     set_code_invalid(regs, RET_DEVICE_NOT_FOUND);
76 }
77
78 // read configuration byte
79 static void
80 handle_1ab108(struct bregs *regs)
81 {
82     regs->cl = pci_config_readb(regs->bx, regs->di);
83     set_code_success(regs);
84 }
85
86 // read configuration word
87 static void
88 handle_1ab109(struct bregs *regs)
89 {
90     regs->cx = pci_config_readw(regs->bx, regs->di);
91     set_code_success(regs);
92 }
93
94 // read configuration dword
95 static void
96 handle_1ab10a(struct bregs *regs)
97 {
98     regs->ecx = pci_config_readl(regs->bx, regs->di);
99     set_code_success(regs);
100 }
101
102 // write configuration byte
103 static void
104 handle_1ab10b(struct bregs *regs)
105 {
106     pci_config_writeb(regs->bx, regs->di, regs->cl);
107     set_code_success(regs);
108 }
109
110 // write configuration word
111 static void
112 handle_1ab10c(struct bregs *regs)
113 {
114     pci_config_writew(regs->bx, regs->di, regs->cx);
115     set_code_success(regs);
116 }
117
118 // write configuration dword
119 static void
120 handle_1ab10d(struct bregs *regs)
121 {
122     pci_config_writel(regs->bx, regs->di, regs->ecx);
123     set_code_success(regs);
124 }
125
126 // get irq routing options
127 static void
128 handle_1ab10e(struct bregs *regs)
129 {
130     struct pir_header *pirtable_g = (void*)(GET_GLOBAL(PirOffset) + 0);
131     if (! pirtable_g) {
132         set_code_invalid(regs, RET_FUNC_NOT_SUPPORTED);
133         return;
134     }
135
136     struct param_s {
137         u16 size;
138         u16 buf_off;
139         u16 buf_seg;
140     } *param_far = (void*)(regs->di+0);
141
142     // Validate and update size.
143     u16 bufsize = GET_FARVAR(regs->es, param_far->size);
144     u16 pirsize = GET_GLOBAL(pirtable_g->size) - sizeof(struct pir_header);
145     SET_FARVAR(regs->es, param_far->size, pirsize);
146     if (bufsize < pirsize) {
147         set_code_invalid(regs, RET_BUFFER_TOO_SMALL);
148         return;
149     }
150
151     // Get dest buffer.
152     void *buf_far = (void*)(GET_FARVAR(regs->es, param_far->buf_off)+0);
153     u16 buf_seg = GET_FARVAR(regs->es, param_far->buf_seg);
154
155     // Memcpy pir table slots to dest buffer.
156     memcpy_far(buf_seg, buf_far
157                , get_global_seg(), pirtable_g->slots
158                , pirsize);
159
160     // XXX - bochs bios sets bx to (1 << 9) | (1 << 11)
161     regs->bx = GET_GLOBAL(pirtable_g->exclusive_irqs);
162     set_code_success(regs);
163 }
164
165 static void
166 handle_1ab1XX(struct bregs *regs)
167 {
168     set_code_unimplemented(regs, RET_FUNC_NOT_SUPPORTED);
169 }
170
171 void
172 handle_1ab1(struct bregs *regs)
173 {
174     //debug_stub(regs);
175
176     if (! CONFIG_PCIBIOS) {
177         set_invalid(regs);
178         return;
179     }
180
181     switch (regs->al) {
182     case 0x01: handle_1ab101(regs); break;
183     case 0x02: handle_1ab102(regs); break;
184     case 0x03: handle_1ab103(regs); break;
185     case 0x08: handle_1ab108(regs); break;
186     case 0x09: handle_1ab109(regs); break;
187     case 0x0a: handle_1ab10a(regs); break;
188     case 0x0b: handle_1ab10b(regs); break;
189     case 0x0c: handle_1ab10c(regs); break;
190     case 0x0d: handle_1ab10d(regs); break;
191     case 0x0e: handle_1ab10e(regs); break;
192     default:   handle_1ab1XX(regs); break;
193     }
194 }