VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/BIOS-new/pcibios.c@ 42030

Last change on this file since 42030 was 42030, checked in by vboxsync, 13 years ago

BIOS: Re-implementing PCI services.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.9 KB
Line 
1/* $Id: pcibios.c 42030 2012-07-05 15:58:14Z vboxsync $ */
2/** @file
3 * PCI BIOS support.
4 */
5
6/*
7 * Copyright (C) 2004-2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include <stdint.h>
19#include <string.h>
20#include "biosint.h"
21#include "inlines.h"
22
23//#define DEBUG_PCI 1 //@todo!
24#if DEBUG_PCI
25# define BX_DEBUG_PCI(...) BX_DEBUG(__VA_ARGS__)
26#else
27# define BX_DEBUG_PCI(...)
28#endif
29
30/* PCI function codes. */
31enum pci_func {
32 PCI_BIOS_PRESENT = 0x01, /* PCI BIOS presence check. */
33 FIND_PCI_DEVICE = 0x02, /* Find PCI device by ID. */
34 FIND_PCI_CLASS_CODE = 0x03, /* Find PCI device by class. */
35 GEN_SPECIAL_CYCLE = 0x06, /* Generate special cycle. */
36 READ_CONFIG_BYTE = 0x08, /* Read a byte from PCI config space. */
37 READ_CONFIG_WORD = 0x09, /* Read a word from PCI config space. */
38 READ_CONFIG_DWORD = 0x0A, /* Read a dword from PCI config space. */
39 WRITE_CONFIG_BYTE = 0x0B, /* Write a byte to PCI config space. */
40 WRITE_CONFIG_WORD = 0x0C, /* Write a word to PCI config space. */
41 WRITE_CONFIG_DWORD = 0x0D, /* Write a dword to PCI config space. */
42 GET_IRQ_ROUTING = 0x0E, /* Get IRQ routing table. */
43 SET_PCI_HW_INT = 0x0F, /* Set PCI hardware interrupt. */
44};
45
46enum pci_error {
47 SUCCESSFUL = 0x00, /* Success. */
48 FUNC_NOT_SUPPORTED = 0x81, /* Unsupported function. */
49 BAD_VENDOR_ID = 0x83, /* Bad vendor ID (all bits set) passed. */
50 DEVICE_NOT_FOUND = 0x86, /* No matching device found. */
51 BAD_REGISTER_NUMBER = 0x87, /* Register number out of range. */
52 SET_FAILED = 0x88, /* Failed to set PCI interrupt. */
53 BUFFER_TOO_SMALL = 0x89 /* Routing table buffer insufficient. */
54};
55
56// @todo: merge with system.c
57#define AX r.gr.u.r16.ax
58#define BX r.gr.u.r16.bx
59#define CX r.gr.u.r16.cx
60#define DX r.gr.u.r16.dx
61#define SI r.gr.u.r16.si
62#define DI r.gr.u.r16.di
63#define BP r.gr.u.r16.bp
64#define SP r.gr.u.r16.sp
65#define FLAGS r.ra.flags.u.r16.flags
66#define EAX r.gr.u.r32.eax
67#define EBX r.gr.u.r32.ebx
68#define ECX r.gr.u.r32.ecx
69#define EDX r.gr.u.r32.edx
70#define ES r.es
71
72/* The 16-bit PCI BIOS service must be callable from both real and protected
73 * mode. In protected mode, the caller must set the CS selector base to F0000h
74 * (but the CS selector value is not specified!). The caller does not always
75 * provide a DS which covers the BIOS segment.
76 *
77 * Unlike APM, there are no provisions for the 32-bit PCI BIOS interface
78 * calling the 16-bit implementation.
79 *
80 * The PCI Firmware Specification requires that the PCI BIOS service is called
81 * with at least 1,024 bytes of stack space available, that interrupts are not
82 * enabled during execution, and that the routines are re-entrant.
83 *
84 * Implementation notes:
85 * - The PCI BIOS interface already uses certain 32-bit registers even in
86 * 16-bit mode. To simplify matters, all 32-bit GPRs are saved/restored and
87 * may be used by helper routines (notably for 32-bit port I/O).
88 */
89
90#define PCI_CFG_ADDR 0xCF8
91#define PCI_CFG_DATA 0xCFC
92
93//@todo: merge with AHCI code
94
95/* Warning: Destroys high bits of EAX. */
96uint32_t inpd(uint16_t port);
97#pragma aux inpd = \
98 ".386" \
99 "in eax, dx" \
100 "mov dx, ax" \
101 "shr eax, 16" \
102 "xchg ax, dx" \
103 parm [dx] value [dx ax] modify nomemory;
104
105/* Warning: Destroys high bits of EAX. */
106void outpd(uint16_t port, uint32_t val);
107#pragma aux outpd = \
108 ".386" \
109 "xchg ax, cx" \
110 "shl eax, 16" \
111 "mov ax, cx" \
112 "out dx, eax" \
113 parm [dx] [cx ax] modify nomemory;
114
115/* Write the CONFIG_ADDRESS register to prepare for data access. Requires
116 * the register offset to be DWORD aligned (low two bits clear). Warning:
117 * destroys high bits of EAX.
118 */
119void pci16_w_addr(uint16_t bus_dev_fn, uint16_t ofs, uint16_t cfg_addr);
120#pragma aux pci16_w_addr = \
121 ".386" \
122 "movzx eax, ax" \
123 "shl eax, 8" \
124 "or eax, 80000000h" \
125 "mov al, bl" \
126 "out dx, eax" \
127 parm [ax] [bx] [dx] modify exact [ax] nomemory;
128
129
130/* Select a PCI configuration register given its offset and bus/dev/fn.
131 * This is largely a wrapper to avoid excessive inlining.
132 */
133void pci16_select_reg(uint16_t bus_dev_fn, uint16_t ofs)
134{
135 pci16_w_addr(bus_dev_fn, ofs & ~3, PCI_CFG_ADDR);
136}
137
138#define PCI_VEN_ID 0x00
139#define PCI_DEV_ID 0x02
140#define PCI_REV_ID 0x08
141#define PCI_CLASS_CODE 0x09
142#define PCI_HEADER_TYPE 0x0E
143
144
145/* Find a specified PCI device, either by vendor+device ID or class.
146 * If index is non-zero, the n-th device will be located.
147 *
148 * Note: This function is somewhat performance critical; since it may
149 * generate a high number of port I/O accesses, it can take a significant
150 * amount of time in cases where the caller is looking for a number of
151 * non-present devices.
152 */
153uint16_t pci16_find_device(uint32_t search_item, uint16_t index, int search_class)
154{
155 uint32_t data;
156 uint16_t bus_dev_fn;
157 uint8_t max_bus;
158 uint8_t hdr_type;
159 int step;
160 int found;
161
162 if (search_class) {
163 BX_DEBUG_PCI("PCI: Find class %08lX index %u\n",
164 search_item, index);
165 } else
166 BX_DEBUG_PCI("PCI: Find device %04X:%04X index %u\n",
167 (uint16_t)search_item, (uint16_t)(search_item >> 16), index);
168
169 bus_dev_fn = 0; /* Start at the beginning. */
170 max_bus = 0; /* Initially assume primary bus only. */
171
172 do {
173 /* For the first function of a device, read the device's header type.
174 * If the header type has all bits set, there's no device. A PCI
175 * multi-function device must implement function 0 and the header type
176 * will be something other than 0xFF. If the header type has the high
177 * bit clear, there is a device but it's not multi-function, so we can
178 * skip probing the next 7 sub-functions.
179 */
180 if ((bus_dev_fn & 7) == 0) {
181 pci16_select_reg(bus_dev_fn, PCI_HEADER_TYPE);
182 hdr_type = inp(PCI_CFG_DATA + (PCI_HEADER_TYPE & 3));
183 if (hdr_type == 0xFF) {
184 bus_dev_fn += 8; /* Skip to next device. */
185 continue;
186 }
187 if (hdr_type & 0x80)
188 step = 1; /* MFD - try every sub-function. */
189 else
190 step = 8; /* No MFD, go to next device after probing. */
191 }
192
193 /* If the header type indicates a bus, we're interested. The secondary
194 * and subordinate bus numbers will indicate which buses are present;
195 * thus we can determine the highest bus number. In the common case,
196 * there will be only the primary bus (i.e. bus 0) and we can avoid
197 * looking at the remaining 255 theoretically present buses. This check
198 * only needs to be done on the primary bus (bridges must report all
199 * bridges behind them).
200 */
201 if ((hdr_type & 7) == 1 && (bus_dev_fn >> 8) == 0) {
202 }
203
204 /* Select the appropriate register. */
205 pci16_select_reg(bus_dev_fn, search_class ? PCI_REV_ID : PCI_VEN_ID);
206 data = inpd(PCI_CFG_DATA);
207 found = 0;
208
209 /* Only 3 bytes are compared for class searches. */
210 if (search_class)
211 data >>= 8;
212
213 BX_DEBUG_PCI("PCI: Data is %08lX @ %02X:%%02X:%01X\n", data,
214 bus_dev_fn >> 8, bus_dev_fn >> 3 & 31, bus_dev_fn & 7);
215
216 if (data == search_item)
217 found = 1;
218
219 /* If device was found but index is non-zero, decrement index and
220 * continue looking. If requested device was found, index will be -1!
221 */
222 if (found && !index--)
223 break;
224
225 bus_dev_fn += step;
226 } while ((bus_dev_fn >> 8) <= max_bus);
227
228 if (index == ~0)
229 BX_DEBUG_PCI("PCI: Device found (%02X:%%02X:%01X)\n", bus_dev_fn >> 8,
230 bus_dev_fn >> 3 & 31, bus_dev_fn & 7);
231
232 return index == ~0 ? bus_dev_fn : ~0;
233}
234
235void BIOSCALL pci16_function(volatile pci_regs_t r)
236{
237 uint16_t device;
238
239 BX_DEBUG_PCI("PCI: AX=%04X BX=%04X CX=%04X\n", AX, BX, CX);
240
241 SET_AH(SUCCESSFUL); /* Assume success. */
242 CLEAR_CF();
243
244 switch (GET_AL()) {
245 case PCI_BIOS_PRESENT:
246 AX = 0x0001; /* Configuration mechanism #1 supported. */
247 BX = 0x0210; /* Version 2.1. */
248 //@todo: return true max bus # in CL
249 CX = 0; /* Maximum bus number. */
250 EDX = 'P' | ('C' << 8) | ((uint32_t)'I' << 16) | ((uint32_t)' ' << 24);
251 break;
252 case FIND_PCI_DEVICE:
253 /* Vendor ID FFFFh is reserved so that non-present devices can
254 * be easily detected.
255 */
256 if (DX == ~0) {
257 SET_AH(BAD_VENDOR_ID);
258 SET_CF();
259 } else {
260 device = pci16_find_device(DX | (uint32_t)CX << 16, SI, 0);
261 if (device == ~0) {
262 SET_AH(DEVICE_NOT_FOUND);
263 SET_CF();
264 } else {
265 BX = device;
266 }
267 }
268 break;
269 case FIND_PCI_CLASS_CODE:
270 device = pci16_find_device(ECX, SI, 1);
271 if (device == ~0) {
272 SET_AH(DEVICE_NOT_FOUND);
273 SET_CF();
274 } else {
275 BX = device;
276 }
277 break;
278 case READ_CONFIG_BYTE:
279 case READ_CONFIG_WORD:
280 case READ_CONFIG_DWORD:
281 case WRITE_CONFIG_BYTE:
282 case WRITE_CONFIG_WORD:
283 case WRITE_CONFIG_DWORD:
284 if (DI >= 256) {
285 SET_AH(BAD_REGISTER_NUMBER);
286 SET_CF();
287 } else {
288 pci16_select_reg(BX, DI);
289 switch (GET_AL()) {
290 case READ_CONFIG_BYTE:
291 SET_CL(inp(PCI_CFG_DATA + (DI & 3)));
292 break;
293 case READ_CONFIG_WORD:
294 CX = inpw(PCI_CFG_DATA + (DI & 2));
295 break;
296 case READ_CONFIG_DWORD:
297 ECX = inpd(PCI_CFG_DATA);
298 break;
299 case WRITE_CONFIG_BYTE:
300 outp(PCI_CFG_DATA + (DI & 3), GET_CL());
301 break;
302 case WRITE_CONFIG_WORD:
303 outpw(PCI_CFG_DATA + (DI & 2), CX);
304 break;
305 case WRITE_CONFIG_DWORD:
306 outpd(PCI_CFG_DATA, ECX);
307 break;
308 }
309 }
310 break;
311 default:
312 BX_INFO("PCI: Unsupported function AX=%04X BX=%04X called\n", AX, BX);
313 SET_AH(FUNC_NOT_SUPPORTED);
314 SET_CF();
315 }
316}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette