Overhaul PCI config functions.
[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 GPLv3 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
14 #define RET_FUNC_NOT_SUPPORTED 0x81
15 #define RET_BAD_VENDOR_ID      0x83
16 #define RET_DEVICE_NOT_FOUND   0x86
17 #define RET_BUFFER_TOO_SMALL   0x89
18
19 // installation check
20 static void
21 handle_1ab101(struct bregs *regs)
22 {
23     regs->al = 0x01; // Flags - "Config Mechanism #1" supported.
24     regs->bx = 0x0210; // PCI version 2.10
25     regs->cl = pci_bdf_to_bus(GET_VAR(CS, MaxBDF) - 1);
26     regs->edx = 0x20494350; // "PCI "
27     // XXX - bochs bios code sets edi to point to 32bit code - but no
28     // reference to this in spec.
29     set_code_success(regs);
30 }
31
32 // find pci device
33 static void
34 handle_1ab102(struct bregs *regs)
35 {
36     int bdf = -1;
37     int count = regs->si;
38     do {
39         bdf = pci_find_device(regs->dx, regs->cx, bdf+1);
40         if (bdf < 0) {
41             set_code_fail(regs, RET_DEVICE_NOT_FOUND);
42             return;
43         }
44     } while (count--);
45
46     regs->bx = bdf;
47     set_code_success(regs);
48 }
49
50 // find class code
51 static void
52 handle_1ab103(struct bregs *regs)
53 {
54     int bdf = -1;
55     int count = regs->si;
56     do {
57         bdf = pci_find_classprog(regs->ecx, bdf+1);
58         if (bdf < 0) {
59             set_code_fail(regs, RET_DEVICE_NOT_FOUND);
60             return;
61         }
62     } while (count--);
63
64     regs->bx = bdf;
65     set_code_success(regs);
66 }
67
68 // read configuration byte
69 static void
70 handle_1ab108(struct bregs *regs)
71 {
72     regs->cl = pci_config_readb(regs->bx, regs->di);
73     set_code_success(regs);
74 }
75
76 // read configuration word
77 static void
78 handle_1ab109(struct bregs *regs)
79 {
80     regs->cx = pci_config_readw(regs->bx, regs->di);
81     set_code_success(regs);
82 }
83
84 // read configuration dword
85 static void
86 handle_1ab10a(struct bregs *regs)
87 {
88     regs->ecx = pci_config_readl(regs->bx, regs->di);
89     set_code_success(regs);
90 }
91
92 // write configuration byte
93 static void
94 handle_1ab10b(struct bregs *regs)
95 {
96     pci_config_writeb(regs->bx, regs->di, regs->cl);
97     set_code_success(regs);
98 }
99
100 // write configuration word
101 static void
102 handle_1ab10c(struct bregs *regs)
103 {
104     pci_config_writew(regs->bx, regs->di, regs->cx);
105     set_code_success(regs);
106 }
107
108 // write configuration dword
109 static void
110 handle_1ab10d(struct bregs *regs)
111 {
112     pci_config_writel(regs->bx, regs->di, regs->ecx);
113     set_code_success(regs);
114 }
115
116 // get irq routing options
117 static void
118 handle_1ab10e(struct bregs *regs)
119 {
120     struct pir_header *pirtable_far = GET_EBDA(pir_loc);
121     if (! pirtable_far) {
122         set_code_fail(regs, RET_FUNC_NOT_SUPPORTED);
123         return;
124     }
125
126     // Validate and update size.
127     u16 size = GET_FARVAR(regs->es, *(u16*)(regs->di+0));
128     u16 pirsize = (GET_FARPTR(pirtable_far->size)
129                    - sizeof(struct pir_header));
130     SET_FARVAR(regs->es, *(u16*)(regs->di+0), pirsize);
131     if (size < pirsize) {
132         set_code_fail(regs, RET_BUFFER_TOO_SMALL);
133         return;
134     }
135
136     // Get dest buffer.
137     u16 d = (GET_FARVAR(regs->es, *(u16*)(regs->di+2)) + 0);
138     u16 destseg = GET_FARVAR(regs->es, *(u16*)(regs->di+4));
139
140     // Memcpy pir table slots to dest buffer.
141     memcpy_far(MAKE_FARPTR(destseg, d), pirtable_far, pirsize);
142
143     // XXX - bochs bios sets bx to (1 << 9) | (1 << 11)
144     regs->bx = GET_FARPTR(pirtable_far->exclusive_irqs);
145     set_code_success(regs);
146 }
147
148 static void
149 handle_1ab1XX(struct bregs *regs)
150 {
151     set_code_fail(regs, RET_FUNC_NOT_SUPPORTED);
152 }
153
154 void
155 handle_1ab1(struct bregs *regs)
156 {
157     //debug_stub(regs);
158
159     if (! CONFIG_PCIBIOS) {
160         set_fail(regs);
161         return;
162     }
163
164     switch (regs->al) {
165     case 0x01: handle_1ab101(regs); break;
166     case 0x02: handle_1ab102(regs); break;
167     case 0x03: handle_1ab103(regs); break;
168     case 0x08: handle_1ab108(regs); break;
169     case 0x09: handle_1ab109(regs); break;
170     case 0x0a: handle_1ab10a(regs); break;
171     case 0x0b: handle_1ab10b(regs); break;
172     case 0x0c: handle_1ab10c(regs); break;
173     case 0x0d: handle_1ab10d(regs); break;
174     case 0x0e: handle_1ab10e(regs); break;
175     default:   handle_1ab1XX(regs); break;
176     }
177 }