VirtualBox

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

Last change on this file since 91881 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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

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