blob: f084fa1086971563e9cfdfedb3cf59cf6c9e7555 [file] [log] [blame]
Kevin O'Connord25810a2008-06-12 22:16:35 -04001// PCI BIOS (int 1a/b1) calls
Kevin O'Connora0dc2962008-03-16 14:29:32 -04002//
3// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
4// Copyright (C) 2002 MandrakeSoft S.A.
5//
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05006// This file may be distributed under the terms of the GNU LGPLv3 license.
Kevin O'Connora0dc2962008-03-16 14:29:32 -04007
8#include "types.h" // u32
9#include "util.h" // handle_1ab1
10#include "pci.h" // pci_config_readl
Kevin O'Connor9521e262008-07-04 13:04:29 -040011#include "bregs.h" // struct bregs
Kevin O'Connor4bc49972012-05-13 22:58:08 -040012#include "biosvar.h" // GET_GLOBAL
Kevin O'Connor4132e022008-12-04 19:39:10 -050013#include "pci_regs.h" // PCI_VENDOR_ID
Kevin O'Connora0dc2962008-03-16 14:29:32 -040014
Kevin O'Connor871e0a02009-12-30 12:14:53 -050015// romlayout.S
Kevin O'Connor47c8e312011-07-10 22:57:32 -040016extern void entry_bios32(void);
17extern void entry_pcibios32(void);
Kevin O'Connor871e0a02009-12-30 12:14:53 -050018
Kevin O'Connora0dc2962008-03-16 14:29:32 -040019#define RET_FUNC_NOT_SUPPORTED 0x81
20#define RET_BAD_VENDOR_ID 0x83
21#define RET_DEVICE_NOT_FOUND 0x86
22#define RET_BUFFER_TOO_SMALL 0x89
23
24// installation check
25static void
26handle_1ab101(struct bregs *regs)
27{
Kevin O'Connorcde7a582008-08-17 11:08:46 -040028 regs->al = 0x01; // Flags - "Config Mechanism #1" supported.
29 regs->bx = 0x0210; // PCI version 2.10
Kevin O'Connor0f654a92011-07-02 14:32:11 -040030 regs->cl = GET_GLOBAL(MaxPCIBus);
Kevin O'Connora0dc2962008-03-16 14:29:32 -040031 regs->edx = 0x20494350; // "PCI "
Kevin O'Connor47c8e312011-07-10 22:57:32 -040032 regs->edi = (u32)entry_pcibios32 + BUILD_BIOS_ADDR;
Kevin O'Connorcde7a582008-08-17 11:08:46 -040033 set_code_success(regs);
Kevin O'Connora0dc2962008-03-16 14:29:32 -040034}
35
36// find pci device
37static void
38handle_1ab102(struct bregs *regs)
39{
Kevin O'Connor4132e022008-12-04 19:39:10 -050040 u32 id = (regs->cx << 16) | regs->dx;
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050041 int count = regs->si;
Kevin O'Connor0f654a92011-07-02 14:32:11 -040042 int bus = -1;
43 while (bus < GET_GLOBAL(MaxPCIBus)) {
44 bus++;
Kevin O'Connor2b333e42011-07-02 14:49:41 -040045 int bdf;
46 foreachbdf(bdf, bus) {
Kevin O'Connor0f654a92011-07-02 14:32:11 -040047 u32 v = pci_config_readl(bdf, PCI_VENDOR_ID);
48 if (v != id)
49 continue;
50 if (count--)
51 continue;
52 regs->bx = bdf;
53 set_code_success(regs);
54 return;
55 }
Kevin O'Connor4132e022008-12-04 19:39:10 -050056 }
Kevin O'Connordfefeb52009-12-13 13:04:17 -050057 set_code_invalid(regs, RET_DEVICE_NOT_FOUND);
Kevin O'Connora0dc2962008-03-16 14:29:32 -040058}
59
60// find class code
61static void
62handle_1ab103(struct bregs *regs)
63{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050064 int count = regs->si;
Kevin O'Connor4132e022008-12-04 19:39:10 -050065 u32 classprog = regs->ecx;
Kevin O'Connor0f654a92011-07-02 14:32:11 -040066 int bus = -1;
67 while (bus < GET_GLOBAL(MaxPCIBus)) {
68 bus++;
Kevin O'Connor2b333e42011-07-02 14:49:41 -040069 int bdf;
70 foreachbdf(bdf, bus) {
Kevin O'Connor0f654a92011-07-02 14:32:11 -040071 u32 v = pci_config_readl(bdf, PCI_CLASS_REVISION);
72 if ((v>>8) != classprog)
73 continue;
74 if (count--)
75 continue;
76 regs->bx = bdf;
77 set_code_success(regs);
78 return;
79 }
Kevin O'Connor4132e022008-12-04 19:39:10 -050080 }
Kevin O'Connordfefeb52009-12-13 13:04:17 -050081 set_code_invalid(regs, RET_DEVICE_NOT_FOUND);
Kevin O'Connora0dc2962008-03-16 14:29:32 -040082}
83
84// read configuration byte
85static void
86handle_1ab108(struct bregs *regs)
87{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050088 regs->cl = pci_config_readb(regs->bx, regs->di);
Kevin O'Connora0dc2962008-03-16 14:29:32 -040089 set_code_success(regs);
90}
91
92// read configuration word
93static void
94handle_1ab109(struct bregs *regs)
95{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050096 regs->cx = pci_config_readw(regs->bx, regs->di);
Kevin O'Connora0dc2962008-03-16 14:29:32 -040097 set_code_success(regs);
98}
99
100// read configuration dword
101static void
102handle_1ab10a(struct bregs *regs)
103{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -0500104 regs->ecx = pci_config_readl(regs->bx, regs->di);
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400105 set_code_success(regs);
106}
107
108// write configuration byte
109static void
110handle_1ab10b(struct bregs *regs)
111{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -0500112 pci_config_writeb(regs->bx, regs->di, regs->cl);
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400113 set_code_success(regs);
114}
115
116// write configuration word
117static void
118handle_1ab10c(struct bregs *regs)
119{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -0500120 pci_config_writew(regs->bx, regs->di, regs->cx);
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400121 set_code_success(regs);
122}
123
124// write configuration dword
125static void
126handle_1ab10d(struct bregs *regs)
127{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -0500128 pci_config_writel(regs->bx, regs->di, regs->ecx);
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400129 set_code_success(regs);
130}
131
132// get irq routing options
133static void
134handle_1ab10e(struct bregs *regs)
135{
Kevin O'Connorbfa02cd2012-06-09 13:36:45 -0400136 struct pir_header *pirtable_gf = GET_GLOBAL(PirAddr);
137 if (! pirtable_gf) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500138 set_code_invalid(regs, RET_FUNC_NOT_SUPPORTED);
Kevin O'Connor0f803e42008-05-24 23:07:16 -0400139 return;
140 }
Kevin O'Connorbfa02cd2012-06-09 13:36:45 -0400141 struct pir_header *pirtable_g = GLOBALFLAT2GLOBAL(pirtable_gf);
Kevin O'Connor0f803e42008-05-24 23:07:16 -0400142
Kevin O'Connord99908e2009-01-21 19:14:49 -0500143 struct param_s {
144 u16 size;
145 u16 buf_off;
146 u16 buf_seg;
147 } *param_far = (void*)(regs->di+0);
148
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400149 // Validate and update size.
Kevin O'Connord99908e2009-01-21 19:14:49 -0500150 u16 bufsize = GET_FARVAR(regs->es, param_far->size);
151 u16 pirsize = GET_GLOBAL(pirtable_g->size) - sizeof(struct pir_header);
152 SET_FARVAR(regs->es, param_far->size, pirsize);
153 if (bufsize < pirsize) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500154 set_code_invalid(regs, RET_BUFFER_TOO_SMALL);
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400155 return;
156 }
157
158 // Get dest buffer.
Kevin O'Connord99908e2009-01-21 19:14:49 -0500159 void *buf_far = (void*)(GET_FARVAR(regs->es, param_far->buf_off)+0);
160 u16 buf_seg = GET_FARVAR(regs->es, param_far->buf_seg);
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400161
162 // Memcpy pir table slots to dest buffer.
Kevin O'Connord99908e2009-01-21 19:14:49 -0500163 memcpy_far(buf_seg, buf_far
Kevin O'Connor871e0a02009-12-30 12:14:53 -0500164 , get_global_seg()
165 , (void*)(pirtable_g->slots) + get_global_offset()
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500166 , pirsize);
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400167
168 // XXX - bochs bios sets bx to (1 << 9) | (1 << 11)
Kevin O'Connor51358db2008-12-28 21:50:29 -0500169 regs->bx = GET_GLOBAL(pirtable_g->exclusive_irqs);
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400170 set_code_success(regs);
171}
172
173static void
174handle_1ab1XX(struct bregs *regs)
175{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500176 set_code_unimplemented(regs, RET_FUNC_NOT_SUPPORTED);
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400177}
178
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400179void
180handle_1ab1(struct bregs *regs)
181{
182 //debug_stub(regs);
183
184 if (! CONFIG_PCIBIOS) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500185 set_invalid(regs);
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400186 return;
187 }
188
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400189 switch (regs->al) {
190 case 0x01: handle_1ab101(regs); break;
191 case 0x02: handle_1ab102(regs); break;
192 case 0x03: handle_1ab103(regs); break;
193 case 0x08: handle_1ab108(regs); break;
194 case 0x09: handle_1ab109(regs); break;
195 case 0x0a: handle_1ab10a(regs); break;
196 case 0x0b: handle_1ab10b(regs); break;
197 case 0x0c: handle_1ab10c(regs); break;
198 case 0x0d: handle_1ab10d(regs); break;
199 case 0x0e: handle_1ab10e(regs); break;
200 default: handle_1ab1XX(regs); break;
201 }
202}
Kevin O'Connor871e0a02009-12-30 12:14:53 -0500203
204
205/****************************************************************
206 * 32bit interface
207 ****************************************************************/
208
Kevin O'Connor871e0a02009-12-30 12:14:53 -0500209// Entry point for 32bit pci bios functions.
210void VISIBLE32SEG
211handle_pcibios32(struct bregs *regs)
212{
213 debug_enter(regs, DEBUG_HDL_pcibios32);
214 handle_1ab1(regs);
215}
Kevin O'Connor871e0a02009-12-30 12:14:53 -0500216
217struct bios32_s {
218 u32 signature;
219 u32 entry;
220 u8 version;
221 u8 length;
222 u8 checksum;
223 u8 reserved[5];
224} PACKED;
225
226struct bios32_s BIOS32HEADER __aligned(16) VAR16EXPORT = {
227 .signature = 0x5f32335f, // _32_
228 .length = sizeof(BIOS32HEADER) / 16,
229};
230
231void
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500232bios32_init(void)
Kevin O'Connor871e0a02009-12-30 12:14:53 -0500233{
234 dprintf(3, "init bios32\n");
235
Kevin O'Connor47c8e312011-07-10 22:57:32 -0400236 BIOS32HEADER.entry = (u32)entry_bios32;
Kevin O'Connor871e0a02009-12-30 12:14:53 -0500237 BIOS32HEADER.checksum -= checksum(&BIOS32HEADER, sizeof(BIOS32HEADER));
238}