blob: 81735e3dd6d1b8bbecc598181799192ce5089c36 [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
Kevin O'Connor4bc49972012-05-13 22:58:08 -04008#include "biosvar.h" // GET_GLOBAL
Kevin O'Connor2d2fa312013-09-14 21:55:26 -04009#include "bregs.h" // struct bregs
10#include "hw/pci.h" // pci_config_readl
Kevin O'Connor4d8510c2016-02-03 01:28:20 -050011#include "hw/pcidevice.h" // MaxPCIBus
Kevin O'Connor5d369d82013-09-02 20:48:46 -040012#include "hw/pci_regs.h" // PCI_VENDOR_ID
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040013#include "output.h" // dprintf
Kevin O'Connorc5e06fb2013-09-14 22:22:28 -040014#include "std/pirtable.h" // struct pir_header
Kevin O'Connorfa9c66a2013-09-14 19:10:40 -040015#include "string.h" // checksum
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040016#include "util.h" // handle_1ab1
Kevin O'Connora0dc2962008-03-16 14:29:32 -040017
Kevin O'Connor871e0a02009-12-30 12:14:53 -050018// romlayout.S
Kevin O'Connor47c8e312011-07-10 22:57:32 -040019extern void entry_bios32(void);
20extern void entry_pcibios32(void);
Kevin O'Connor871e0a02009-12-30 12:14:53 -050021
Kevin O'Connora0dc2962008-03-16 14:29:32 -040022#define RET_FUNC_NOT_SUPPORTED 0x81
23#define RET_BAD_VENDOR_ID 0x83
24#define RET_DEVICE_NOT_FOUND 0x86
25#define RET_BUFFER_TOO_SMALL 0x89
26
27// installation check
28static void
29handle_1ab101(struct bregs *regs)
30{
Kevin O'Connorcde7a582008-08-17 11:08:46 -040031 regs->al = 0x01; // Flags - "Config Mechanism #1" supported.
32 regs->bx = 0x0210; // PCI version 2.10
Kevin O'Connor0f654a92011-07-02 14:32:11 -040033 regs->cl = GET_GLOBAL(MaxPCIBus);
Kevin O'Connora0dc2962008-03-16 14:29:32 -040034 regs->edx = 0x20494350; // "PCI "
Kevin O'Connor47c8e312011-07-10 22:57:32 -040035 regs->edi = (u32)entry_pcibios32 + BUILD_BIOS_ADDR;
Kevin O'Connorcde7a582008-08-17 11:08:46 -040036 set_code_success(regs);
Kevin O'Connora0dc2962008-03-16 14:29:32 -040037}
38
39// find pci device
40static void
41handle_1ab102(struct bregs *regs)
42{
Kevin O'Connor4132e022008-12-04 19:39:10 -050043 u32 id = (regs->cx << 16) | regs->dx;
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050044 int count = regs->si;
Kevin O'Connor0f654a92011-07-02 14:32:11 -040045 int bus = -1;
46 while (bus < GET_GLOBAL(MaxPCIBus)) {
47 bus++;
Kevin O'Connor2b333e42011-07-02 14:49:41 -040048 int bdf;
49 foreachbdf(bdf, bus) {
Kevin O'Connor0f654a92011-07-02 14:32:11 -040050 u32 v = pci_config_readl(bdf, PCI_VENDOR_ID);
51 if (v != id)
52 continue;
53 if (count--)
54 continue;
55 regs->bx = bdf;
56 set_code_success(regs);
57 return;
58 }
Kevin O'Connor4132e022008-12-04 19:39:10 -050059 }
Kevin O'Connordfefeb52009-12-13 13:04:17 -050060 set_code_invalid(regs, RET_DEVICE_NOT_FOUND);
Kevin O'Connora0dc2962008-03-16 14:29:32 -040061}
62
63// find class code
64static void
65handle_1ab103(struct bregs *regs)
66{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050067 int count = regs->si;
Kevin O'Connor4132e022008-12-04 19:39:10 -050068 u32 classprog = regs->ecx;
Kevin O'Connor0f654a92011-07-02 14:32:11 -040069 int bus = -1;
70 while (bus < GET_GLOBAL(MaxPCIBus)) {
71 bus++;
Kevin O'Connor2b333e42011-07-02 14:49:41 -040072 int bdf;
73 foreachbdf(bdf, bus) {
Kevin O'Connor0f654a92011-07-02 14:32:11 -040074 u32 v = pci_config_readl(bdf, PCI_CLASS_REVISION);
75 if ((v>>8) != classprog)
76 continue;
77 if (count--)
78 continue;
79 regs->bx = bdf;
80 set_code_success(regs);
81 return;
82 }
Kevin O'Connor4132e022008-12-04 19:39:10 -050083 }
Kevin O'Connordfefeb52009-12-13 13:04:17 -050084 set_code_invalid(regs, RET_DEVICE_NOT_FOUND);
Kevin O'Connora0dc2962008-03-16 14:29:32 -040085}
86
87// read configuration byte
88static void
89handle_1ab108(struct bregs *regs)
90{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050091 regs->cl = pci_config_readb(regs->bx, regs->di);
Kevin O'Connora0dc2962008-03-16 14:29:32 -040092 set_code_success(regs);
93}
94
95// read configuration word
96static void
97handle_1ab109(struct bregs *regs)
98{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050099 regs->cx = pci_config_readw(regs->bx, regs->di);
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400100 set_code_success(regs);
101}
102
103// read configuration dword
104static void
105handle_1ab10a(struct bregs *regs)
106{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -0500107 regs->ecx = pci_config_readl(regs->bx, regs->di);
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400108 set_code_success(regs);
109}
110
111// write configuration byte
112static void
113handle_1ab10b(struct bregs *regs)
114{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -0500115 pci_config_writeb(regs->bx, regs->di, regs->cl);
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400116 set_code_success(regs);
117}
118
119// write configuration word
120static void
121handle_1ab10c(struct bregs *regs)
122{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -0500123 pci_config_writew(regs->bx, regs->di, regs->cx);
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400124 set_code_success(regs);
125}
126
127// write configuration dword
128static void
129handle_1ab10d(struct bregs *regs)
130{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -0500131 pci_config_writel(regs->bx, regs->di, regs->ecx);
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400132 set_code_success(regs);
133}
134
135// get irq routing options
136static void
137handle_1ab10e(struct bregs *regs)
138{
Kevin O'Connorbfa02cd2012-06-09 13:36:45 -0400139 struct pir_header *pirtable_gf = GET_GLOBAL(PirAddr);
140 if (! pirtable_gf) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500141 set_code_invalid(regs, RET_FUNC_NOT_SUPPORTED);
Kevin O'Connor0f803e42008-05-24 23:07:16 -0400142 return;
143 }
Kevin O'Connorbfa02cd2012-06-09 13:36:45 -0400144 struct pir_header *pirtable_g = GLOBALFLAT2GLOBAL(pirtable_gf);
Kevin O'Connor0f803e42008-05-24 23:07:16 -0400145
Kevin O'Connord99908e2009-01-21 19:14:49 -0500146 struct param_s {
147 u16 size;
148 u16 buf_off;
149 u16 buf_seg;
150 } *param_far = (void*)(regs->di+0);
151
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400152 // Validate and update size.
Kevin O'Connord99908e2009-01-21 19:14:49 -0500153 u16 bufsize = GET_FARVAR(regs->es, param_far->size);
154 u16 pirsize = GET_GLOBAL(pirtable_g->size) - sizeof(struct pir_header);
155 SET_FARVAR(regs->es, param_far->size, pirsize);
156 if (bufsize < pirsize) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500157 set_code_invalid(regs, RET_BUFFER_TOO_SMALL);
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400158 return;
159 }
160
161 // Get dest buffer.
Kevin O'Connord99908e2009-01-21 19:14:49 -0500162 void *buf_far = (void*)(GET_FARVAR(regs->es, param_far->buf_off)+0);
163 u16 buf_seg = GET_FARVAR(regs->es, param_far->buf_seg);
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400164
165 // Memcpy pir table slots to dest buffer.
Kevin O'Connord99908e2009-01-21 19:14:49 -0500166 memcpy_far(buf_seg, buf_far
Kevin O'Connor871e0a02009-12-30 12:14:53 -0500167 , get_global_seg()
168 , (void*)(pirtable_g->slots) + get_global_offset()
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500169 , pirsize);
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400170
171 // XXX - bochs bios sets bx to (1 << 9) | (1 << 11)
Kevin O'Connor51358db2008-12-28 21:50:29 -0500172 regs->bx = GET_GLOBAL(pirtable_g->exclusive_irqs);
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400173 set_code_success(regs);
174}
175
176static void
177handle_1ab1XX(struct bregs *regs)
178{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500179 set_code_unimplemented(regs, RET_FUNC_NOT_SUPPORTED);
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400180}
181
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400182void
183handle_1ab1(struct bregs *regs)
184{
185 //debug_stub(regs);
186
187 if (! CONFIG_PCIBIOS) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500188 set_invalid(regs);
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400189 return;
190 }
191
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400192 switch (regs->al) {
193 case 0x01: handle_1ab101(regs); break;
194 case 0x02: handle_1ab102(regs); break;
195 case 0x03: handle_1ab103(regs); break;
196 case 0x08: handle_1ab108(regs); break;
197 case 0x09: handle_1ab109(regs); break;
198 case 0x0a: handle_1ab10a(regs); break;
199 case 0x0b: handle_1ab10b(regs); break;
200 case 0x0c: handle_1ab10c(regs); break;
201 case 0x0d: handle_1ab10d(regs); break;
202 case 0x0e: handle_1ab10e(regs); break;
203 default: handle_1ab1XX(regs); break;
204 }
205}
Kevin O'Connor871e0a02009-12-30 12:14:53 -0500206
Kevin O'Connor922aa1b2013-03-02 04:02:11 -0500207// Entry point for pci bios functions.
208void VISIBLE16 VISIBLE32SEG
209handle_pcibios(struct bregs *regs)
210{
211 debug_enter(regs, DEBUG_HDL_pcibios);
212 handle_1ab1(regs);
213}
214
Kevin O'Connor871e0a02009-12-30 12:14:53 -0500215
216/****************************************************************
217 * 32bit interface
218 ****************************************************************/
219
Kevin O'Connor871e0a02009-12-30 12:14:53 -0500220struct bios32_s {
221 u32 signature;
222 u32 entry;
223 u8 version;
224 u8 length;
225 u8 checksum;
226 u8 reserved[5];
227} PACKED;
228
Kevin O'Connor89a2f962013-02-18 23:36:03 -0500229struct bios32_s BIOS32HEADER __aligned(16) VARFSEG = {
Kevin O'Connor871e0a02009-12-30 12:14:53 -0500230 .signature = 0x5f32335f, // _32_
231 .length = sizeof(BIOS32HEADER) / 16,
232};
233
234void
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500235bios32_init(void)
Kevin O'Connor871e0a02009-12-30 12:14:53 -0500236{
237 dprintf(3, "init bios32\n");
238
Kevin O'Connor47c8e312011-07-10 22:57:32 -0400239 BIOS32HEADER.entry = (u32)entry_bios32;
Kevin O'Connor871e0a02009-12-30 12:14:53 -0500240 BIOS32HEADER.checksum -= checksum(&BIOS32HEADER, sizeof(BIOS32HEADER));
241}