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