VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/BIOS/pcibios.c

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.1 KB
RevLine 
[42030]1/* $Id: pcibios.c 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * PCI BIOS support.
4 */
5
6/*
[98103]7 * Copyright (C) 2004-2023 Oracle and/or its affiliates.
[42030]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
[42030]26 */
27
28#include <stdint.h>
29#include <string.h>
30#include "biosint.h"
31#include "inlines.h"
32
33#if DEBUG_PCI
34# define BX_DEBUG_PCI(...) BX_DEBUG(__VA_ARGS__)
35#else
36# define BX_DEBUG_PCI(...)
37#endif
38
39/* PCI function codes. */
40enum pci_func {
41 PCI_BIOS_PRESENT = 0x01, /* PCI BIOS presence check. */
42 FIND_PCI_DEVICE = 0x02, /* Find PCI device by ID. */
43 FIND_PCI_CLASS_CODE = 0x03, /* Find PCI device by class. */
44 GEN_SPECIAL_CYCLE = 0x06, /* Generate special cycle. */
45 READ_CONFIG_BYTE = 0x08, /* Read a byte from PCI config space. */
46 READ_CONFIG_WORD = 0x09, /* Read a word from PCI config space. */
47 READ_CONFIG_DWORD = 0x0A, /* Read a dword from PCI config space. */
48 WRITE_CONFIG_BYTE = 0x0B, /* Write a byte to PCI config space. */
49 WRITE_CONFIG_WORD = 0x0C, /* Write a word to PCI config space. */
50 WRITE_CONFIG_DWORD = 0x0D, /* Write a dword to PCI config space. */
51 GET_IRQ_ROUTING = 0x0E, /* Get IRQ routing table. */
52 SET_PCI_HW_INT = 0x0F, /* Set PCI hardware interrupt. */
53};
54
55enum pci_error {
56 SUCCESSFUL = 0x00, /* Success. */
57 FUNC_NOT_SUPPORTED = 0x81, /* Unsupported function. */
58 BAD_VENDOR_ID = 0x83, /* Bad vendor ID (all bits set) passed. */
59 DEVICE_NOT_FOUND = 0x86, /* No matching device found. */
60 BAD_REGISTER_NUMBER = 0x87, /* Register number out of range. */
61 SET_FAILED = 0x88, /* Failed to set PCI interrupt. */
62 BUFFER_TOO_SMALL = 0x89 /* Routing table buffer insufficient. */
63};
64
[63562]65/// @todo merge with system.c
[42030]66#define AX r.gr.u.r16.ax
67#define BX r.gr.u.r16.bx
68#define CX r.gr.u.r16.cx
69#define DX r.gr.u.r16.dx
70#define SI r.gr.u.r16.si
71#define DI r.gr.u.r16.di
72#define BP r.gr.u.r16.bp
73#define SP r.gr.u.r16.sp
74#define EAX r.gr.u.r32.eax
75#define EBX r.gr.u.r32.ebx
76#define ECX r.gr.u.r32.ecx
77#define EDX r.gr.u.r32.edx
78#define ES r.es
79
80/* The 16-bit PCI BIOS service must be callable from both real and protected
81 * mode. In protected mode, the caller must set the CS selector base to F0000h
82 * (but the CS selector value is not specified!). The caller does not always
83 * provide a DS which covers the BIOS segment.
84 *
85 * Unlike APM, there are no provisions for the 32-bit PCI BIOS interface
86 * calling the 16-bit implementation.
87 *
88 * The PCI Firmware Specification requires that the PCI BIOS service is called
89 * with at least 1,024 bytes of stack space available, that interrupts are not
90 * enabled during execution, and that the routines are re-entrant.
91 *
[48947]92 * Implementation notes:
[42030]93 * - The PCI BIOS interface already uses certain 32-bit registers even in
94 * 16-bit mode. To simplify matters, all 32-bit GPRs are saved/restored and
95 * may be used by helper routines (notably for 32-bit port I/O).
96 */
97
98#define PCI_CFG_ADDR 0xCF8
99#define PCI_CFG_DATA 0xCFC
100
[42127]101#ifdef __386__
102
103#define PCIxx(x) pci32_##x
104
105/* The stack layout is different in 32-bit mode. */
106typedef struct {
107 pushad_regs_t gr;
[42332]108 uint32_t es;
[42127]109 uint32_t flags;
110} pci_regs_t;
111
112#define FLAGS r.flags
113
114/* In 32-bit mode, don't do any output; not technically impossible but needs
115 * a lot of extra code.
116 */
117#undef BX_INFO
118#define BX_INFO(...)
119#undef BX_DEBUG_PCI
120#define BX_DEBUG_PCI(...)
121
122#else
123
124#define PCIxx(x) pci16_##x
125
126typedef struct {
127 pushad_regs_t gr;
[45710]128 uint16_t ds;
[42127]129 uint16_t es;
130 iret_addr_t ra;
131} pci_regs_t;
132
133#define FLAGS r.ra.flags.u.r16.flags
134
135#endif
136
137#ifdef __386__
138
139/* 32-bit code can just use the compiler intrinsics. */
140extern unsigned inpd(unsigned port);
141extern unsigned outpd(unsigned port, unsigned value);
142#pragma intrinsic(inpd,outpd)
143
144#else
145
[63562]146/// @todo merge with AHCI code
[42030]147
148/* Warning: Destroys high bits of EAX. */
149uint32_t inpd(uint16_t port);
150#pragma aux inpd = \
151 ".386" \
152 "in eax, dx" \
153 "mov dx, ax" \
154 "shr eax, 16" \
155 "xchg ax, dx" \
156 parm [dx] value [dx ax] modify nomemory;
157
158/* Warning: Destroys high bits of EAX. */
159void outpd(uint16_t port, uint32_t val);
160#pragma aux outpd = \
161 ".386" \
162 "xchg ax, cx" \
163 "shl eax, 16" \
164 "mov ax, cx" \
165 "out dx, eax" \
166 parm [dx] [cx ax] modify nomemory;
167
[42127]168#endif
169
[42332]170/* PCI IRQ routing expansion buffer descriptor. */
171typedef struct {
172 uint16_t buf_size;
173 uint8_t __far *buf_ptr;
174} pci_route_buf;
175
176/* Defined in assembler module .*/
177extern char pci_routing_table[];
178extern uint16_t pci_routing_table_size;
179
[42030]180/* Write the CONFIG_ADDRESS register to prepare for data access. Requires
181 * the register offset to be DWORD aligned (low two bits clear). Warning:
182 * destroys high bits of EAX.
183 */
184void pci16_w_addr(uint16_t bus_dev_fn, uint16_t ofs, uint16_t cfg_addr);
185#pragma aux pci16_w_addr = \
186 ".386" \
187 "movzx eax, ax" \
188 "shl eax, 8" \
189 "or eax, 80000000h" \
190 "mov al, bl" \
191 "out dx, eax" \
192 parm [ax] [bx] [dx] modify exact [ax] nomemory;
193
194
195/* Select a PCI configuration register given its offset and bus/dev/fn.
196 * This is largely a wrapper to avoid excessive inlining.
197 */
[42127]198void PCIxx(select_reg)(uint16_t bus_dev_fn, uint16_t ofs)
[42030]199{
200 pci16_w_addr(bus_dev_fn, ofs & ~3, PCI_CFG_ADDR);
201}
202
[42127]203/* Selected configuration space offsets. */
[42030]204#define PCI_VEN_ID 0x00
205#define PCI_DEV_ID 0x02
206#define PCI_REV_ID 0x08
207#define PCI_CLASS_CODE 0x09
208#define PCI_HEADER_TYPE 0x0E
[42127]209#define PCI_BRIDGE_SUBORD 0x1A
[42030]210
[42127]211/* To avoid problems with 16-bit code, we reserve the last possible
212 * bus/dev/fn combination (65,535). Upon reaching this location, the
213 * probing will end.
214 */
[42793]215#define BUSDEVFN_NOT_FOUND 0xFFFF
[42030]216
[42793]217/* In the search algorithm, we decrement the device index every time
218 * a matching device is found. If the requested device is indeed found,
219 * the index will have decremented down to -1/0xFFFF.
220 */
221#define INDEX_DEV_FOUND 0xFFFF
222
[42030]223/* Find a specified PCI device, either by vendor+device ID or class.
[78052]224 * If index is non-zero, the n-th device will be located. When searching
225 * by class, the ignore_if flag only compares the base and sub-class code,
226 * ignoring the programming interface code.
[42030]227 *
228 * Note: This function is somewhat performance critical; since it may
229 * generate a high number of port I/O accesses, it can take a significant
230 * amount of time in cases where the caller is looking for a number of
231 * non-present devices.
232 */
[78052]233uint16_t PCIxx(find_device)(uint32_t search_item, uint16_t index, int search_class, int ignore_if)
[42030]234{
235 uint32_t data;
236 uint16_t bus_dev_fn;
237 uint8_t max_bus;
238 uint8_t hdr_type;
[42127]239 uint8_t subordinate;
[42030]240 int step;
241 int found;
242
243 if (search_class) {
244 BX_DEBUG_PCI("PCI: Find class %08lX index %u\n",
245 search_item, index);
[48947]246 } else
247 BX_DEBUG_PCI("PCI: Find device %04X:%04X index %u\n",
[42030]248 (uint16_t)search_item, (uint16_t)(search_item >> 16), index);
249
250 bus_dev_fn = 0; /* Start at the beginning. */
251 max_bus = 0; /* Initially assume primary bus only. */
252
253 do {
254 /* For the first function of a device, read the device's header type.
255 * If the header type has all bits set, there's no device. A PCI
256 * multi-function device must implement function 0 and the header type
257 * will be something other than 0xFF. If the header type has the high
258 * bit clear, there is a device but it's not multi-function, so we can
259 * skip probing the next 7 sub-functions.
260 */
261 if ((bus_dev_fn & 7) == 0) {
[42127]262 PCIxx(select_reg)(bus_dev_fn, PCI_HEADER_TYPE);
[42030]263 hdr_type = inp(PCI_CFG_DATA + (PCI_HEADER_TYPE & 3));
264 if (hdr_type == 0xFF) {
265 bus_dev_fn += 8; /* Skip to next device. */
266 continue;
267 }
268 if (hdr_type & 0x80)
269 step = 1; /* MFD - try every sub-function. */
270 else
271 step = 8; /* No MFD, go to next device after probing. */
272 }
273
274 /* If the header type indicates a bus, we're interested. The secondary
[48947]275 * and subordinate bus numbers will indicate which buses are present;
276 * thus we can determine the highest bus number. In the common case,
[42030]277 * there will be only the primary bus (i.e. bus 0) and we can avoid
278 * looking at the remaining 255 theoretically present buses. This check
[42127]279 * only needs to be done on the primary bus, since bridges must report
280 * all bridges potentially behind them.
[42030]281 */
282 if ((hdr_type & 7) == 1 && (bus_dev_fn >> 8) == 0) {
[42127]283 /* Read the subordinate (last) bridge number. */
284 PCIxx(select_reg)(bus_dev_fn, PCI_BRIDGE_SUBORD);
285 subordinate = inp(PCI_CFG_DATA + (PCI_BRIDGE_SUBORD & 3));
286 if (subordinate > max_bus)
287 max_bus = subordinate;
[42030]288 }
289
290 /* Select the appropriate register. */
[42127]291 PCIxx(select_reg)(bus_dev_fn, search_class ? PCI_REV_ID : PCI_VEN_ID);
[42030]292 data = inpd(PCI_CFG_DATA);
293 found = 0;
294
[78052]295 /* Only 3 or even just 2 bytes are compared for class searches. */
[42030]296 if (search_class)
[78052]297 if (ignore_if)
298 data >>= 16;
299 else
300 data >>= 8;
[42030]301
[42392]302#if 0
[42030]303 BX_DEBUG_PCI("PCI: Data is %08lX @ %02X:%%02X:%01X\n", data,
304 bus_dev_fn >> 8, bus_dev_fn >> 3 & 31, bus_dev_fn & 7);
[42392]305#endif
[42030]306
307 if (data == search_item)
308 found = 1;
309
310 /* If device was found but index is non-zero, decrement index and
311 * continue looking. If requested device was found, index will be -1!
312 */
313 if (found && !index--)
314 break;
315
316 bus_dev_fn += step;
317 } while ((bus_dev_fn >> 8) <= max_bus);
318
[42793]319 if (index == INDEX_DEV_FOUND)
[42030]320 BX_DEBUG_PCI("PCI: Device found (%02X:%%02X:%01X)\n", bus_dev_fn >> 8,
321 bus_dev_fn >> 3 & 31, bus_dev_fn & 7);
322
[42793]323 return index == INDEX_DEV_FOUND ? bus_dev_fn : BUSDEVFN_NOT_FOUND;
[42030]324}
325
[42127]326void BIOSCALL PCIxx(function)(volatile pci_regs_t r)
[42030]327{
[42332]328 pci_route_buf __far *route_buf;
329 uint16_t device;
[42030]330
[42532]331 BX_DEBUG_PCI("PCI: AX=%04X BX=%04X CX=%04X DI=%04X\n", AX, BX, CX, DI);
[42030]332
333 SET_AH(SUCCESSFUL); /* Assume success. */
[48947]334 CLEAR_CF();
[42030]335
336 switch (GET_AL()) {
337 case PCI_BIOS_PRESENT:
338 AX = 0x0001; /* Configuration mechanism #1 supported. */
339 BX = 0x0210; /* Version 2.1. */
[63562]340 /// @todo return true max bus # in CL
[42030]341 CX = 0; /* Maximum bus number. */
342 EDX = 'P' | ('C' << 8) | ((uint32_t)'I' << 16) | ((uint32_t)' ' << 24);
343 break;
344 case FIND_PCI_DEVICE:
345 /* Vendor ID FFFFh is reserved so that non-present devices can
346 * be easily detected.
347 */
[42127]348 if (DX == 0xFFFF) {
[42030]349 SET_AH(BAD_VENDOR_ID);
350 SET_CF();
351 } else {
[78052]352 device = PCIxx(find_device)(DX | (uint32_t)CX << 16, SI, 0, 0);
[42793]353 if (device == BUSDEVFN_NOT_FOUND) {
[42030]354 SET_AH(DEVICE_NOT_FOUND);
355 SET_CF();
356 } else {
357 BX = device;
358 }
359 }
360 break;
361 case FIND_PCI_CLASS_CODE:
[78052]362 device = PCIxx(find_device)(ECX, SI, 1, 0);
[42793]363 if (device == BUSDEVFN_NOT_FOUND) {
[42030]364 SET_AH(DEVICE_NOT_FOUND);
365 SET_CF();
366 } else {
367 BX = device;
368 }
369 break;
370 case READ_CONFIG_BYTE:
371 case READ_CONFIG_WORD:
372 case READ_CONFIG_DWORD:
373 case WRITE_CONFIG_BYTE:
374 case WRITE_CONFIG_WORD:
375 case WRITE_CONFIG_DWORD:
376 if (DI >= 256) {
377 SET_AH(BAD_REGISTER_NUMBER);
378 SET_CF();
379 } else {
[42127]380 PCIxx(select_reg)(BX, DI);
[42030]381 switch (GET_AL()) {
382 case READ_CONFIG_BYTE:
383 SET_CL(inp(PCI_CFG_DATA + (DI & 3)));
384 break;
385 case READ_CONFIG_WORD:
386 CX = inpw(PCI_CFG_DATA + (DI & 2));
387 break;
388 case READ_CONFIG_DWORD:
389 ECX = inpd(PCI_CFG_DATA);
390 break;
391 case WRITE_CONFIG_BYTE:
392 outp(PCI_CFG_DATA + (DI & 3), GET_CL());
393 break;
394 case WRITE_CONFIG_WORD:
395 outpw(PCI_CFG_DATA + (DI & 2), CX);
396 break;
397 case WRITE_CONFIG_DWORD:
398 outpd(PCI_CFG_DATA, ECX);
399 break;
400 }
401 }
402 break;
[42332]403 case GET_IRQ_ROUTING:
404 route_buf = ES :> (void *)DI;
[49067]405 BX_DEBUG_PCI("PCI: Route Buf %04X:%04X size %04X, need %04X (at %04X:%04X)\n",
[45710]406 FP_SEG(route_buf->buf_ptr), FP_OFF(route_buf->buf_ptr),
[49067]407 route_buf->buf_size, pci_routing_table_size, ES, DI);
[42332]408 if (pci_routing_table_size > route_buf->buf_size) {
409 SET_AH(BUFFER_TOO_SMALL);
410 SET_CF();
411 } else {
412 rep_movsb(route_buf->buf_ptr, pci_routing_table, pci_routing_table_size);
[42392]413 /* IRQs 9 and 11 are PCI only. */
414 BX = (1 << 9) | (1 << 11);
[42332]415 }
[45710]416 route_buf->buf_size = pci_routing_table_size;
[42332]417 break;
[42030]418 default:
419 BX_INFO("PCI: Unsupported function AX=%04X BX=%04X called\n", AX, BX);
420 SET_AH(FUNC_NOT_SUPPORTED);
421 SET_CF();
422 }
423}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use