1 /* SPDX-License-Identifier: BSD-2-Clause */
5 #include <arch/byteorder.h>
7 #include "compat/rtas.h"
11 #include <device/device.h>
12 #include <device/pci.h>
13 #include <device/pci_ops.h>
15 /* the device we are working with... */
16 biosemu_device_t bios_device
;
17 //max. 6 BARs and 1 Exp.ROM plus CfgSpace and 3 legacy ranges, plus 2 "special" memory ranges
18 translate_address_t translate_address_array
[13];
28 } __attribute__ ((__packed__
)) assigned_address_t
;
30 #if CONFIG_PCI_OPTION_ROM_RUN_YABEL
31 /* coreboot version */
34 biosemu_dev_get_addr_info(void)
39 u8 bus
= bios_device
.dev
->bus
->secondary
;
40 u16 devfn
= bios_device
.dev
->path
.pci
.devfn
;
42 bios_device
.bus
= bus
;
43 bios_device
.devfn
= devfn
;
45 DEBUG_PRINTF("bus: %x, devfn: %x\n", bus
, devfn
);
46 for (r
= bios_device
.dev
->resource_list
; r
; r
= r
->next
) {
47 translate_address_array
[taa_index
].info
= r
->flags
;
48 translate_address_array
[taa_index
].bus
= bus
;
49 translate_address_array
[taa_index
].devfn
= devfn
;
50 translate_address_array
[taa_index
].cfg_space_offset
=
52 translate_address_array
[taa_index
].address
= r
->base
;
53 translate_address_array
[taa_index
].size
= r
->size
;
54 /* don't translate addresses... all addresses are 1:1 */
55 translate_address_array
[taa_index
].address_offset
= 0;
59 translate_address_array
[taa_index
].info
= IORESOURCE_MEM
| IORESOURCE_READONLY
;
60 translate_address_array
[taa_index
].bus
= bus
;
61 translate_address_array
[taa_index
].devfn
= devfn
;
62 translate_address_array
[taa_index
].cfg_space_offset
= 0x30;
63 translate_address_array
[taa_index
].address
= bios_device
.img_addr
;
64 translate_address_array
[taa_index
].size
= 0; /* TODO: do we need the size? */
65 /* don't translate addresses... all addresses are 1:1 */
66 translate_address_array
[taa_index
].address_offset
= 0;
68 /* legacy ranges if its a VGA card... */
69 if ((bios_device
.dev
->class & 0xFF0000) == 0x030000) {
70 DEBUG_PRINTF("%s: VGA device found, adding legacy resources..."
73 translate_address_array
[taa_index
].info
= IORESOURCE_FIXED
| IORESOURCE_IO
;
74 translate_address_array
[taa_index
].bus
= bus
;
75 translate_address_array
[taa_index
].devfn
= devfn
;
76 translate_address_array
[taa_index
].cfg_space_offset
= 0;
77 translate_address_array
[taa_index
].address
= 0x3b0;
78 translate_address_array
[taa_index
].size
= 0xc;
79 /* don't translate addresses... all addresses are 1:1 */
80 translate_address_array
[taa_index
].address_offset
= 0;
83 translate_address_array
[taa_index
].info
= IORESOURCE_FIXED
| IORESOURCE_IO
;
84 translate_address_array
[taa_index
].bus
= bus
;
85 translate_address_array
[taa_index
].devfn
= devfn
;
86 translate_address_array
[taa_index
].cfg_space_offset
= 0;
87 translate_address_array
[taa_index
].address
= 0x3c0;
88 translate_address_array
[taa_index
].size
= 0x20;
89 /* don't translate addresses... all addresses are 1:1 */
90 translate_address_array
[taa_index
].address_offset
= 0;
92 /* Mem 0xA0000-0xBFFFF */
93 translate_address_array
[taa_index
].info
= IORESOURCE_FIXED
| IORESOURCE_MEM
;
94 translate_address_array
[taa_index
].bus
= bus
;
95 translate_address_array
[taa_index
].devfn
= devfn
;
96 translate_address_array
[taa_index
].cfg_space_offset
= 0;
97 translate_address_array
[taa_index
].address
= 0xa0000;
98 translate_address_array
[taa_index
].size
= 0x20000;
99 /* don't translate addresses... all addresses are 1:1 */
100 translate_address_array
[taa_index
].address_offset
= 0;
103 // store last entry index of translate_address_array
104 taa_last_entry
= taa_index
- 1;
105 #if CONFIG_X86EMU_DEBUG
106 //dump translate_address_array
107 printf("translate_address_array:\n");
108 translate_address_t ta
;
110 for (i
= 0; i
<= taa_last_entry
; i
++) {
111 ta
= translate_address_array
[i
];
113 ("%d: info: %08lx bus: %02x devfn: %02x cfg_space_offset: %02x\n\taddr: %016llx\n\toffs: %016llx\n\tsize: %016llx\n",
114 i
, ta
.info
, ta
.bus
, ta
.devfn
, ta
.cfg_space_offset
,
115 ta
.address
, ta
.address_offset
, ta
.size
);
121 // use translate_address_dev and get_puid from net-snk's net_support.c
122 void translate_address_dev(u64
*, phandle_t
);
123 u64
get_puid(phandle_t node
);
126 // scan all addresses assigned to the device ("assigned-addresses" and "reg")
127 // store in translate_address_array for faster translation using dev_translate_address
129 biosemu_dev_get_addr_info(void)
131 // get bus/dev/fn from assigned-addresses
133 //max. 6 BARs and 1 Exp.ROM plus CfgSpace and 3 legacy ranges
134 assigned_address_t buf
[11];
136 of_getprop(bios_device
.phandle
, "assigned-addresses", buf
,
138 bios_device
.bus
= buf
[0].bus
;
139 bios_device
.devfn
= buf
[0].devfn
;
140 DEBUG_PRINTF("bus: %x, devfn: %x\n", bios_device
.bus
,
142 //store address translations for all assigned-addresses and regs in
143 //translate_address_array for faster translation later on...
145 // index to insert data into translate_address_array
148 for (i
= 0; i
< (len
/ sizeof(assigned_address_t
)); i
++, taa_index
++) {
149 //copy all info stored in assigned-addresses
150 translate_address_array
[taa_index
].info
= buf
[i
].info
;
151 translate_address_array
[taa_index
].bus
= buf
[i
].bus
;
152 translate_address_array
[taa_index
].devfn
= buf
[i
].devfn
;
153 translate_address_array
[taa_index
].cfg_space_offset
=
154 buf
[i
].cfg_space_offset
;
155 translate_address_array
[taa_index
].address
= buf
[i
].address
;
156 translate_address_array
[taa_index
].size
= buf
[i
].size
;
157 // translate first address and store it as address_offset
158 address_offset
= buf
[i
].address
;
159 translate_address_dev(&address_offset
, bios_device
.phandle
);
160 translate_address_array
[taa_index
].address_offset
=
161 address_offset
- buf
[i
].address
;
164 len
= of_getprop(bios_device
.phandle
, "reg", buf
, sizeof(buf
));
165 for (i
= 0; i
< (len
/ sizeof(assigned_address_t
)); i
++) {
166 if ((buf
[i
].size
== 0) || (buf
[i
].cfg_space_offset
!= 0)) {
167 // we don't care for ranges with size 0 and
168 // BARs and Expansion ROM must be in assigned-addresses... so in reg
169 // we only look for those without config space offset set...
170 // i.e. the legacy ranges
173 //copy all info stored in assigned-addresses
174 translate_address_array
[taa_index
].info
= buf
[i
].info
;
175 translate_address_array
[taa_index
].bus
= buf
[i
].bus
;
176 translate_address_array
[taa_index
].devfn
= buf
[i
].devfn
;
177 translate_address_array
[taa_index
].cfg_space_offset
=
178 buf
[i
].cfg_space_offset
;
179 translate_address_array
[taa_index
].address
= buf
[i
].address
;
180 translate_address_array
[taa_index
].size
= buf
[i
].size
;
181 // translate first address and store it as address_offset
182 address_offset
= buf
[i
].address
;
183 translate_address_dev(&address_offset
, bios_device
.phandle
);
184 translate_address_array
[taa_index
].address_offset
=
185 address_offset
- buf
[i
].address
;
188 // store last entry index of translate_address_array
189 taa_last_entry
= taa_index
- 1;
190 #if CONFIG_X86EMU_DEBUG
191 //dump translate_address_array
192 printf("translate_address_array:\n");
193 translate_address_t ta
;
194 for (i
= 0; i
<= taa_last_entry
; i
++) {
195 ta
= translate_address_array
[i
];
197 ("%d: %02x%02x%02x%02x\n\taddr: %016llx\n\toffs: %016llx\n\tsize: %016llx\n",
198 i
, ta
.info
, ta
.bus
, ta
.devfn
, ta
.cfg_space_offset
,
199 ta
.address
, ta
.address_offset
, ta
.size
);
205 // "special memory" is a hack to make some parts of memory fall through to real memory
206 // (ie. no translation). Necessary if option ROMs attempt DMA there, map registers or
207 // do similarly crazy things.
209 biosemu_add_special_memory(u32 start
, u32 size
)
212 int taa_index
= ++taa_last_entry
;
213 translate_address_array
[taa_index
].info
= IORESOURCE_FIXED
| IORESOURCE_MEM
;
214 translate_address_array
[taa_index
].bus
= 0;
215 translate_address_array
[taa_index
].devfn
= 0;
216 translate_address_array
[taa_index
].cfg_space_offset
= 0;
217 translate_address_array
[taa_index
].address
= start
;
218 translate_address_array
[taa_index
].size
= size
;
219 /* don't translate addresses... all addresses are 1:1 */
220 translate_address_array
[taa_index
].address_offset
= 0;
222 printf("TODO: Add special memory handler for %x[%x]\n", start
, size
);
225 #if !CONFIG_PCI_OPTION_ROM_RUN_YABEL
226 // to simulate accesses to legacy VGA Memory (0xA0000-0xBFFFF)
227 // we look for the first prefetchable memory BAR, if no prefetchable BAR found,
228 // we use the first memory BAR
229 // dev_translate_addr will translate accesses to the legacy VGA Memory into the found vmem BAR
231 biosemu_dev_find_vmem_addr(void)
234 translate_address_t ta
;
235 s8 tai_np
= -1, tai_p
= -1; // translate_address_array index for non-prefetchable and prefetchable memory
236 //search backwards to find first entry
237 for (i
= taa_last_entry
; i
>= 0; i
--) {
238 ta
= translate_address_array
[i
];
239 if ((ta
.cfg_space_offset
>= 0x10)
240 && (ta
.cfg_space_offset
<= 0x24)) {
242 if ((ta
.info
& 0x03) >= 0x02) {
245 if ((ta
.info
& 0x40) != 0) {
253 ta
= translate_address_array
[tai_p
];
254 bios_device
.vmem_addr
= ta
.address
;
255 bios_device
.vmem_size
= ta
.size
;
257 ("%s: Found prefetchable Virtual Legacy Memory BAR: %llx, size: %llx\n",
258 __func__
, bios_device
.vmem_addr
,
259 bios_device
.vmem_size
);
260 } else if (tai_np
!= -1) {
261 ta
= translate_address_array
[tai_np
];
262 bios_device
.vmem_addr
= ta
.address
;
263 bios_device
.vmem_size
= ta
.size
;
265 ("%s: Found non-prefetchable Virtual Legacy Memory BAR: %llx, size: %llx",
266 __func__
, bios_device
.vmem_addr
,
267 bios_device
.vmem_size
);
270 //bios_device.vmem_size = 0;
274 biosemu_dev_get_puid(void)
277 bios_device
.puid
= get_puid(bios_device
.phandle
);
278 DEBUG_PRINTF("puid: 0x%llx\n", bios_device
.puid
);
283 biosemu_dev_get_device_vendor_id(void)
287 #if CONFIG_PCI_OPTION_ROM_RUN_YABEL
288 pci_config_0
= pci_read_config32(bios_device
.dev
, 0x0);
291 rtas_pci_config_read(bios_device
.puid
, 4, bios_device
.bus
,
292 bios_device
.devfn
, 0x0);
294 bios_device
.pci_device_id
=
295 (u16
) ((pci_config_0
& 0xFFFF0000) >> 16);
296 bios_device
.pci_vendor_id
= (u16
) (pci_config_0
& 0x0000FFFF);
297 DEBUG_PRINTF("PCI Device ID: %04x, PCI Vendor ID: %x\n",
298 bios_device
.pci_device_id
, bios_device
.pci_vendor_id
);
301 /* Check whether the device has a valid Expansion ROM and search the PCI Data
302 * Structure and any Expansion ROM Header (using dev_scan_exp_header()) for
303 * needed information. If the rom_addr parameter is != 0, it is the address of
304 * the Expansion ROM image and will be used, if it is == 0, the Expansion ROM
305 * BAR address will be used.
308 biosemu_dev_check_exprom(unsigned long rom_base_addr
)
312 translate_address_t ta
;
314 pci_data_struct_t pci_ds
;
315 if (rom_base_addr
== 0) {
316 // check for ExpROM Address (Offset 30) in taa
317 for (i
= 0; i
<= taa_last_entry
; i
++) {
318 ta
= translate_address_array
[i
];
319 if (ta
.cfg_space_offset
== 0x30) {
321 rom_base_addr
= ta
.address
+ ta
.address_offset
;
326 /* In the ROM there could be multiple Expansion ROM Images... start
327 * searching them for an x86 image.
330 if (rom_base_addr
== 0) {
331 printf("Error: no Expansion ROM address found!\n");
335 u16 rom_signature
= in16le((void *) rom_base_addr
);
337 if (rom_signature
!= 0xaa55) {
339 ("Error: invalid Expansion ROM signature: %02x!\n",
340 *((u16
*) rom_base_addr
));
344 // at offset 0x18 is the (16bit little-endian) pointer to the PCI Data Structure
345 pci_ds_offset
= in16le((void *) (rom_base_addr
+ 0x18));
346 //copy the PCI Data Structure
347 memcpy(&pci_ds
, (void *) (rom_base_addr
+ pci_ds_offset
),
350 #if CONFIG_X86EMU_DEBUG
351 DEBUG_PRINTF("PCI Data Structure @%lx:\n",
352 rom_base_addr
+ pci_ds_offset
);
353 dump((void *) &pci_ds
, sizeof(pci_ds
));
355 if (strncmp((const char *) pci_ds
.signature
, "PCIR", 4) != 0) {
356 printf("Invalid PCI Data Structure found!\n");
359 //little-endian conversion
360 pci_ds
.vendor_id
= in16le(&pci_ds
.vendor_id
);
361 pci_ds
.device_id
= in16le(&pci_ds
.device_id
);
362 pci_ds
.img_length
= in16le(&pci_ds
.img_length
);
363 pci_ds
.pci_ds_length
= in16le(&pci_ds
.pci_ds_length
);
364 #ifdef DO_THIS_TEST_TWICE
365 if (pci_ds
.vendor_id
!= bios_device
.pci_vendor_id
) {
367 ("Image has invalid Vendor ID: %04x, expected: %04x\n",
368 pci_ds
.vendor_id
, bios_device
.pci_vendor_id
);
371 if (pci_ds
.device_id
!= bios_device
.pci_device_id
) {
373 ("Image has invalid Device ID: %04x, expected: %04x\n",
374 pci_ds
.device_id
, bios_device
.pci_device_id
);
378 DEBUG_PRINTF("Image Length: %d\n", pci_ds
.img_length
* 512);
379 DEBUG_PRINTF("Image Code Type: %d\n", pci_ds
.code_type
);
380 if (pci_ds
.code_type
== 0) {
382 //store image address and image length in bios_device struct
383 bios_device
.img_addr
= rom_base_addr
;
384 bios_device
.img_size
= pci_ds
.img_length
* 512;
385 // we found the image, exit the loop
388 // no x86 image, check next image (if any)
389 rom_base_addr
+= pci_ds
.img_length
* 512;
391 if ((pci_ds
.indicator
& 0x80) == 0x80) {
392 //last image found, exit the loop
393 DEBUG_PRINTF("Last PCI Expansion ROM Image found.\n");
397 while (bios_device
.img_addr
== 0);
398 // in case we did not find a valid x86 Expansion ROM Image
399 if (bios_device
.img_addr
== 0) {
400 printf("Error: no valid x86 Expansion ROM Image found!\n");
409 biosemu_dev_init(struct device
* device
)
412 //init bios_device struct
413 DEBUG_PRINTF("%s\n", __func__
);
414 memset(&bios_device
, 0, sizeof(bios_device
));
416 #if !CONFIG_PCI_OPTION_ROM_RUN_YABEL
417 bios_device
.ihandle
= of_open(device_name
);
418 if (bios_device
.ihandle
== 0) {
419 DEBUG_PRINTF("%s is no valid device!\n", device_name
);
422 bios_device
.phandle
= of_finddevice(device_name
);
424 bios_device
.dev
= device
;
426 biosemu_dev_get_addr_info();
427 #if !CONFIG_PCI_OPTION_ROM_RUN_YABEL
428 biosemu_dev_find_vmem_addr();
429 biosemu_dev_get_puid();
431 biosemu_dev_get_device_vendor_id();
435 // translate address function using translate_address_array assembled
436 // by dev_get_addr_info... MUCH faster than calling translate_address_dev
437 // and accessing client interface for every translation...
438 // returns: 0 if addr not found in translate_address_array, 1 if found.
440 biosemu_dev_translate_address(int type
, unsigned long * addr
)
443 translate_address_t ta
;
444 #if !CONFIG_PCI_OPTION_ROM_RUN_YABEL
445 /* we don't need this hack for coreboot... we can access legacy areas */
446 //check if it is an access to legacy VGA Mem... if it is, map the address
447 //to the vmem BAR and then translate it...
448 // (translation info provided by Ben Herrenschmidt)
449 // NOTE: the translation seems to only work for NVIDIA cards... but it is needed
450 // to make some NVIDIA cards work at all...
451 if ((bios_device
.vmem_size
> 0)
452 && ((*addr
>= 0xA0000) && (*addr
< 0xB8000))) {
453 *addr
= (*addr
- 0xA0000) * 4 + 2 + bios_device
.vmem_addr
;
455 if ((bios_device
.vmem_size
> 0)
456 && ((*addr
>= 0xB8000) && (*addr
< 0xC0000))) {
457 u8 shift
= *addr
& 1;
459 *addr
= (*addr
- 0xB8000) * 4 + shift
+ bios_device
.vmem_addr
;
462 for (i
= 0; i
<= taa_last_entry
; i
++) {
463 ta
= translate_address_array
[i
];
464 if ((*addr
>= ta
.address
) && (*addr
<= (ta
.address
+ ta
.size
)) && (ta
.info
& type
)) {
465 *addr
+= ta
.address_offset
;