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