Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | /* |
2 | * ========== Copyright Header Begin ========================================== | |
3 | * | |
4 | * Hypervisor Software File: res_piu_pcie.c | |
5 | * | |
6 | * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. | |
7 | * | |
8 | * - Do no alter or remove copyright notices | |
9 | * | |
10 | * - Redistribution and use of this software in source and binary forms, with | |
11 | * or without modification, are permitted provided that the following | |
12 | * conditions are met: | |
13 | * | |
14 | * - Redistribution of source code must retain the above copyright notice, | |
15 | * this list of conditions and the following disclaimer. | |
16 | * | |
17 | * - Redistribution in binary form must reproduce the above copyright notice, | |
18 | * this list of conditions and the following disclaimer in the | |
19 | * documentation and/or other materials provided with the distribution. | |
20 | * | |
21 | * Neither the name of Sun Microsystems, Inc. or the names of contributors | |
22 | * may be used to endorse or promote products derived from this software | |
23 | * without specific prior written permission. | |
24 | * | |
25 | * This software is provided "AS IS," without a warranty of any kind. | |
26 | * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, | |
27 | * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A | |
28 | * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN | |
29 | * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR | |
30 | * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR | |
31 | * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN | |
32 | * OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR | |
33 | * FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE | |
34 | * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, | |
35 | * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF | |
36 | * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. | |
37 | * | |
38 | * You acknowledge that this software is not designed, licensed or | |
39 | * intended for use in the design, construction, operation or maintenance of | |
40 | * any nuclear facility. | |
41 | * | |
42 | * ========== Copyright Header End ============================================ | |
43 | */ | |
44 | /* | |
45 | * Copyright 2007 Sun Microsystems, Inc. All rights reserved. | |
46 | * Use is subject to license terms. | |
47 | */ | |
48 | ||
49 | ||
50 | #pragma ident "@(#)res_piu_pcie.c 1.6 07/06/07 SMI" | |
51 | ||
52 | #include <stdarg.h> | |
53 | ||
54 | #include <sys/htypes.h> | |
55 | #include <hypervisor.h> | |
56 | #include <traps.h> | |
57 | #include <sun4v/asi.h> | |
58 | #include <sun4v/errs_defs.h> | |
59 | #include <vdev_ops.h> | |
60 | #include <vdev_intr.h> | |
61 | #include <config.h> | |
62 | #include <ncs.h> | |
63 | #include <mmu.h> | |
64 | #include <cyclic.h> | |
65 | #include <vcpu.h> | |
66 | #include <strand.h> | |
67 | #include <guest.h> | |
68 | #include <segments.h> | |
69 | #include <memory.h> | |
70 | #include <pcie.h> | |
71 | #include <support.h> | |
72 | #include <md.h> | |
73 | #include <abort.h> | |
74 | #include <proto.h> | |
75 | #include <piu.h> | |
76 | #include <fpga.h> | |
77 | ||
78 | #ifdef CONFIG_PCIE | |
79 | ||
80 | #ifdef CONFIG_PIU | |
81 | extern const struct piu_cookie piu_dev[]; | |
82 | void c_piu_leaf_soft_reset(const struct piu_cookie *, int root); | |
83 | #endif | |
84 | ||
85 | void plat_config_pcie_bypass(pcie_device_t *pciep); | |
86 | static void config_pcie_bypass(guest_t *guestp, int bus_id); | |
87 | static void setup_bypass_mappings(int bus_id, uint64_t base_pa, uint64_t size); | |
88 | static void clear_pcie_bypass(int bus_id); | |
89 | ||
90 | /* | |
91 | * (re)-configuration code to handle HV PIU PCI-E resources | |
92 | */ | |
93 | ||
94 | #define MAX_RESETS 10 | |
95 | ||
96 | void | |
97 | config_platform_pcie() | |
98 | { | |
99 | DBG(c_printf("config_piu()\n")); | |
100 | } | |
101 | ||
102 | void | |
103 | reset_platform_pcie_busses(guest_t *guestp, pcie_device_t *pciep) | |
104 | { | |
105 | extern const struct piu_cookie piu_dev[]; | |
106 | int i; | |
107 | ||
108 | /* reset attached devices like the PCI busses */ | |
109 | pciep = config.pcie_busses; | |
110 | for (i = 0; i < NUM_PCIE_BUSSES; i++) { | |
111 | ||
112 | DBGBR(c_printf("pcie 0x%x assigned to guest 0x%x\n", | |
113 | &pciep[i], pciep[i].guestp)); | |
114 | ||
115 | /* if bus is assigned to this guest, soft reset the bus */ | |
116 | if (pciep[i].guestp == guestp) { | |
117 | DBGBR(c_printf("Soft Reset PCI leaf 0x%x cookie 0x%x\n", | |
118 | i, piu_dev[i].pcie)); | |
119 | ||
120 | #if 0 | |
121 | /* | |
122 | * we dont care if this works really - if it fails | |
123 | * the bus is likely dead. The reset on guest restart | |
124 | * will likely set it up to function again ... | |
125 | */ | |
126 | (void) pcie_bus_reset(i); | |
127 | #endif | |
128 | ||
129 | /* | |
130 | * regardless of the bus reset we shutdown PIU's | |
131 | * IOMMU and interrupt logic for this bus | |
132 | */ | |
133 | c_piu_leaf_soft_reset( | |
134 | (struct piu_cookie *)&piu_dev[i], i); | |
135 | } | |
136 | } | |
137 | } | |
138 | ||
139 | #ifdef CONFIG_IOBYPASS | |
140 | ||
141 | /* | |
142 | * For a guest which is allowed direct access to the I/O bridges | |
143 | * this sets up the segments for the I/O physical addresses. | |
144 | */ | |
145 | ||
146 | #define BASE_1 0x8800000000 | |
147 | #define LIMIT_1 0x8900000000 | |
148 | #define BASE_2 0xc000000000 | |
149 | #define LIMIT_2 0xff00000000 | |
150 | ||
151 | #define ASSIGN_PIU_SEGMENTS(_guestp) \ | |
152 | assign_ra2pa_segments(_guestp, BASE_1, \ | |
153 | LIMIT_1 - BASE_1, 0, IO_SEGMENT); \ | |
154 | assign_ra2pa_segments(_guestp, BASE_2, \ | |
155 | LIMIT_2 - BASE_2, 0, IO_SEGMENT); \ | |
156 | assign_ra2pa_segments(guestp, FPGA_UART_BASE, \ | |
157 | FPGA_UART_LIMIT - FPGA_UART_BASE, 0, IO_SEGMENT); | |
158 | ||
159 | #define UNASSIGN_PIU_SEGMENTS(_guestp) \ | |
160 | assign_ra2pa_segments(_guestp, BASE_1, \ | |
161 | INVALID_SEGMENT_SIZE, 0, INVALID_SEGMENT); \ | |
162 | assign_ra2pa_segments(_guestp, BASE_2, \ | |
163 | INVALID_SEGMENT_SIZE, 0, INVALID_SEGMENT); \ | |
164 | assign_ra2pa_segments(guestp, FPGA_UART_BASE, \ | |
165 | FPGA_UART_LIMIT - FPGA_UART_BASE, 0, INVALID_SEGMENT); | |
166 | #else | |
167 | ||
168 | #define PIU_BASE (0xc810000000) | |
169 | #define PIU_LIMIT (0xd000000000) | |
170 | ||
171 | #define ASSIGN_PIU_SEGMENTS(_guestp) \ | |
172 | assign_ra2pa_segments(_guestp, PIU_BASE, \ | |
173 | PIU_LIMIT - PIU_BASE, 0, IO_SEGMENT); \ | |
174 | assign_ra2pa_segments(guestp, FPGA_UART_BASE, \ | |
175 | FPGA_UART_LIMIT - FPGA_UART_BASE, 0, IO_SEGMENT); | |
176 | ||
177 | #define UNASSIGN_PIU_SEGMENTS(_guestp) \ | |
178 | assign_ra2pa_segments(_guestp, PIU_BASE, \ | |
179 | INVALID_SEGMENT_SIZE, 0, INVALID_SEGMENT); \ | |
180 | assign_ra2pa_segments(guestp, FPGA_UART_BASE, \ | |
181 | FPGA_UART_LIMIT - FPGA_UART_BASE, 0, INVALID_SEGMENT); | |
182 | ||
183 | #endif /* CONFIG_IOBYPASS */ | |
184 | ||
185 | void | |
186 | config_a_guest_pcie_bus(pcie_device_t *pciep) | |
187 | { | |
188 | guest_t *guestp; | |
189 | int id, vinobase, x; | |
190 | uint8_t devid; | |
191 | ||
192 | guestp = pciep->guestp; | |
193 | id = pciep->id; | |
194 | ||
195 | ASSERT(guestp != NULL); | |
196 | ||
197 | devid = (pciep->cfg_handle) >> DEVCFGPA_SHIFT; | |
198 | vinobase = pciep->cfg_handle; | |
199 | ||
200 | plat_config_pcie_bypass(pciep); | |
201 | ||
202 | switch (id) { | |
203 | case (0): | |
204 | guestp->dev2inst[devid] = DEVOPS_PIU(0); | |
205 | ASSIGN_PIU_SEGMENTS(guestp); | |
206 | DBGBR(c_printf("\tNOTICE pcie LEAF 0x%x " \ | |
207 | "configured for guest 0x%x\n", id, | |
208 | guestp->guestid)); | |
209 | break; | |
210 | default: | |
211 | DBGBR(c_printf("\tWARNING pcie 0x%x not " | |
212 | "supported for guest 0x%x\n", id, guestp->guestid)); | |
213 | /* should probably panic here */ | |
214 | break; | |
215 | } | |
216 | ||
217 | for (x = 0; x < NINOSPERDEV; x++) { | |
218 | guestp->vino2inst.vino[vinobase + x] = | |
219 | config_pcie_vinos.vino[id][x]; | |
220 | } | |
221 | } | |
222 | ||
223 | void | |
224 | unconfig_a_guest_pcie_bus(pcie_device_t *pciep) | |
225 | { | |
226 | guest_t *guestp; | |
227 | int id, vinobase, x; | |
228 | uint8_t devid; | |
229 | ||
230 | guestp = pciep->guestp; | |
231 | id = pciep->id; | |
232 | ||
233 | ASSERT(guestp != NULL); | |
234 | ||
235 | devid = (pciep->cfg_handle) >> DEVCFGPA_SHIFT; | |
236 | vinobase = pciep->cfg_handle; | |
237 | ||
238 | guestp->dev2inst[devid] = DEVOPS_RESERVED; | |
239 | for (x = 0; x < NINOSPERDEV; x++) { | |
240 | guestp->vino2inst.vino[vinobase + x] = DEVOPS_RESERVED; | |
241 | } | |
242 | ||
243 | /* | |
244 | * We clear out the PCI I/O address segments by setting their size | |
245 | * to INVALID_SEGMENT_SIZE and flags to INVALID_SEGMENT. | |
246 | */ | |
247 | switch (id) { | |
248 | case (0): | |
249 | UNASSIGN_PIU_SEGMENTS(guestp); | |
250 | break; | |
251 | default: | |
252 | break; | |
253 | } | |
254 | } | |
255 | ||
256 | #define CFGWR(_offset, _size, _val) \ | |
257 | do { \ | |
258 | if (!pci_config_put(firep, (_offset), _size, _val)) { \ | |
259 | DBGBR(c_printf("CFGWR fail "#_offset \ | |
260 | " ("#_size") <- "#_val"\n")); \ | |
261 | return (false); \ | |
262 | } \ | |
263 | } while (0) | |
264 | ||
265 | ||
266 | static void config_piu_pcie_bus(int busnum); | |
267 | ||
268 | /* | |
269 | * This function does an initial test of the PCI-E links attached to | |
270 | * PIU - primarily to determine if we have any PLX 8532 AA switches | |
271 | * directly attached ... if so, we need to determine which ports are | |
272 | * live so that we can guarantee a re-train in the event of a reset | |
273 | */ | |
274 | void | |
275 | config_pcie() | |
276 | { | |
277 | ||
278 | } | |
279 | ||
280 | /* | |
281 | * This function examines the specified PCI-E bus on PIU, and looks | |
282 | * for PLX 8532 AA switches ... and determines which downstream ports on | |
283 | * those switches are live. | |
284 | */ | |
285 | ||
286 | void | |
287 | config_piu_pcie_bus(int busnum) | |
288 | { | |
289 | uint64_t res; | |
290 | piu_dev_t *piup; | |
291 | ||
292 | ASSERT(busnum == 0); | |
293 | ||
294 | piup = (piu_dev_t *)&piu_dev[busnum]; | |
295 | ||
296 | /* Becomes true after bus is used, false after it is reset */ | |
297 | piup->needs_warm_reset = false; | |
298 | piup->blacklist = false; | |
299 | ||
300 | DBGBR(c_printf("Probing PIU bus %d\n", busnum)); | |
301 | ||
302 | /* | |
303 | * If the link is down, we don't do anything | |
304 | * since it may mean there is no HW on the bus. | |
305 | */ | |
306 | ||
307 | /* | |
308 | * Just bail if nothing connected to the bus | |
309 | * We indicate a failure on the bus in this case to ensure | |
310 | * that we mark the bus to be ignored. Even though the link | |
311 | * status is checked on each config write, we try to prevent the | |
312 | * case where a link is down here and comes up later ... without | |
313 | * the bus being properly reset. By returning a failure here we hope | |
314 | * that the HV marks the bus as dead and prevents further | |
315 | * guest accesses. | |
316 | */ | |
317 | if (!is_piu_port_link_up(piup)) | |
318 | return; | |
319 | ||
320 | /* | |
321 | * If we are dealing with something other than | |
322 | * a PLX or bridge (network card, for example) | |
323 | * we simply toggle the link down and up. | |
324 | * Otherwise we have to do the full secondary | |
325 | * reset technique | |
326 | */ | |
327 | ||
328 | /* | |
329 | * We look for a bridge under PIU. | |
330 | * If the read fails, we have a problem ... abort for now, but | |
331 | * need to handle this as simply the bus being inactive. | |
332 | * We may need a PIU reset to resolve this - i.e. power cycle. | |
333 | */ | |
334 | ||
335 | if (!pci_config_get(piup, UPST_CFG_BASE + CFG_CLASS_CODE, 4, &res)) { | |
336 | DBGBR(c_printf("Bridge read failed ... abandoning bus\n")); | |
337 | return; | |
338 | } | |
339 | ||
340 | DBGBR(c_printf("Bridge class code 0x%x\n", res)); | |
341 | ||
342 | /* | |
343 | * If the device class code indicates there is no bridge, then we | |
344 | * simply stop here since we're not going to be doing a | |
345 | * secondary reset. | |
346 | */ | |
347 | ||
348 | if ((res>>8) != BRIDGE_CLASS_CODE) { | |
349 | DBGBR(c_printf("Not a bridge ...")); | |
350 | return; | |
351 | } | |
352 | ||
353 | /* | |
354 | * We discovered a bridge, but is it a PLX ? indicating | |
355 | * ... if this read fails something bad | |
356 | * has happened given that we already read from the device. | |
357 | * In this case we abandon the bus ! | |
358 | */ | |
359 | ||
360 | if (!pci_config_get(piup, UPST_CFG_BASE + 0, 4, &res)) { | |
361 | DBGBR(c_printf("Bridge vendor ID read failed !!\n")); | |
362 | return; | |
363 | } | |
364 | ||
365 | DBGBR(c_printf("1st bridge vendorid = 0x%x\n", res)); | |
366 | DBGBR(c_printf("Done probing bus 0x%x\n", busnum)); | |
367 | } | |
368 | ||
369 | /* | |
370 | * This function resets a given PIU leaf | |
371 | * | |
372 | * (It should be based on resetting a given pci-e resource). | |
373 | * | |
374 | * Inputs: | |
375 | * | |
376 | * bus - root complex (0 = leaf A) | |
377 | * | |
378 | * Returns true on success, false on failure. | |
379 | * Caller should mark bus unusable after failure. | |
380 | */ | |
381 | bool_t | |
382 | pcie_bus_reset(int busnum) | |
383 | { | |
384 | uint64_t brcode; | |
385 | piu_dev_t *piup; | |
386 | ||
387 | piup = (piu_dev_t *)&piu_dev[busnum]; | |
388 | ||
389 | /* | |
390 | * If the bus just came out of a power on cycle | |
391 | * then we don't need to reset it again here. | |
392 | */ | |
393 | if (!piup->needs_warm_reset) { | |
394 | piup->needs_warm_reset = true; | |
395 | DBGBR(c_printf("PCI-E warm reset not required\r\n")); | |
396 | return (true); | |
397 | } | |
398 | ||
399 | if (piup->blacklist) { | |
400 | DBGBR(c_printf("PCI-E bus 0x%x blacklisted \r\n", busnum)); | |
401 | return (false); | |
402 | } | |
403 | ||
404 | DBGBR(c_printf("Resetting bus %d (piup=0x%x)\n", | |
405 | busnum, (uint64_t)piup)); | |
406 | ||
407 | /* | |
408 | * If the link is down, we don't do anything | |
409 | * since it may mean there is no HW on the bus. | |
410 | */ | |
411 | /* | |
412 | * Just bail if nothing connected to the bus | |
413 | * We indicate a failure on the bus in this case to ensure | |
414 | * that we mark the bus to be ignored. Even though the link | |
415 | * status is checked on each config write, we try to prevent the | |
416 | * case where a link is down here and comes up later ... without | |
417 | * the bus being properly reset. By returning a failure here we hope | |
418 | * that the HV marks the bus as dead and prevents further | |
419 | * guest accesses. | |
420 | */ | |
421 | if (!is_piu_port_link_up(piup)) { | |
422 | DBGBR(c_printf("PCI-E bus 0x%x link down \r\n", busnum)); | |
423 | goto fail; | |
424 | } | |
425 | ||
426 | DBGBR(c_printf("PCI-E bus 0x%x link is up \r\n", busnum)); | |
427 | ||
428 | ||
429 | /* | |
430 | * If we are dealing with something other than | |
431 | * a bridge (network card, for example) | |
432 | * we simply toggle the link down and up. | |
433 | * Otherwise we have to do the full secondary | |
434 | * reset technique | |
435 | */ | |
436 | ||
437 | /* | |
438 | * We look for a bridge under PIU. | |
439 | * If the read fails, we have a problem ... abort for now, but | |
440 | * need to handle this as simply the bus being inactive. | |
441 | * We may need a fire reset to resolve this - i.e. power cycle. | |
442 | */ | |
443 | ||
444 | if (!pci_config_get(piup, UPST_CFG_BASE + CFG_CLASS_CODE, | |
445 | 4, &brcode)) { | |
446 | DBGBR(c_printf("Bridge read failed ... abandoning bus\n")); | |
447 | goto fail; | |
448 | } | |
449 | ||
450 | DBGBR(c_printf("Bridge class code 0x%x\n", brcode)); | |
451 | ||
452 | /* | |
453 | * If the device class code indicates there is no bridge, then we | |
454 | * simply reset the link by toggling it | |
455 | */ | |
456 | ||
457 | if ((brcode>>8) != BRIDGE_CLASS_CODE) { | |
458 | DBGBR(c_printf("Not a bridge ...")); | |
459 | DBGBR(c_printf("...toggle link\n")); | |
460 | ||
461 | if (!piu_link_down(piup)) | |
462 | goto fail; | |
463 | ||
464 | piu_reset_onboard_devices(); | |
465 | ||
466 | if (!piu_link_up(piup)) | |
467 | goto fail; | |
468 | } | |
469 | ||
470 | return (true); | |
471 | ||
472 | fail: | |
473 | piup->blacklist = true; | |
474 | c_hvabort(-1); | |
475 | return (false); | |
476 | } | |
477 | ||
478 | void | |
479 | piu_reset_onboard_devices(void) | |
480 | { | |
481 | #ifdef CONFIG_FPGA | |
482 | volatile uint16_t *fpga_device_id = (uint16_t *)FPGA_DEVICE_ID; | |
483 | volatile uint8_t *fpga_reset_control = | |
484 | (uint8_t *)FPGA_LDOM_RESET_CONTROL; | |
485 | volatile uint8_t *fpga_slot_reset_control = | |
486 | (uint8_t *)FPGA_LDOM_SLOT_RESET_CONTROL; | |
487 | volatile uint8_t *fpga_device_present = | |
488 | (uint8_t *)FPGA_DEVICE_PRESENT; | |
489 | volatile uint16_t fpga_major_version; | |
490 | volatile uint8_t fpga_devices; | |
491 | ||
492 | /* | |
493 | * GPIO reset support | |
494 | * If FPGA has GPIO reset support(checked with major revision | |
495 | * ID), then twiddle the reset pins for the onboard devices | |
496 | * and pcie slots. Hold reset for 200 msec, then wait for | |
497 | * 1 second. | |
498 | */ | |
499 | fpga_major_version = (*fpga_device_id >> FPGA_ID_MAJOR_ID_SHIFT) | |
500 | & FPGA_ID_MAJOR_ID_MASK; | |
501 | if (fpga_major_version >= FPGA_MIN_MAJOR_ID_RESET_SUPPORT) { | |
502 | DBGBR(c_printf("link toggled down\n")); | |
503 | fpga_devices = *fpga_device_present; | |
504 | *fpga_reset_control = FPGA_LDOM_RESET_CONTROL_MASK; | |
505 | *fpga_slot_reset_control = fpga_devices; | |
506 | c_usleep(200000); /* 200ms */ | |
507 | *fpga_reset_control = 0; | |
508 | *fpga_slot_reset_control = 0; | |
509 | c_usleep(1000000); /* 1sec */ | |
510 | ||
511 | DBGBR(c_printf("PCI-E bus has devices " \ | |
512 | "present at slots 0x%x\r\n", fpga_devices)); | |
513 | } | |
514 | #endif /* CONFIG_FPGA */ | |
515 | } | |
516 | ||
517 | void | |
518 | init_pcie_buses(void) | |
519 | { | |
520 | DBGPE(c_printf("initializing pcie buses\n")); | |
521 | } | |
522 | ||
523 | static void | |
524 | setup_bypass_mappings(int bus_id, uint64_t bypass_pa, uint64_t size) | |
525 | { | |
526 | extern const uint64_t piu_iotsb1; | |
527 | ||
528 | piu_dev_t *piup; | |
529 | uint64_t *ttep; | |
530 | uint64_t nttes; | |
531 | int i; | |
532 | ||
533 | ASSERT(bus_id == 0); | |
534 | piup = (piu_dev_t *)&(piu_dev[bus_id]); | |
535 | ||
536 | nttes = size >> IOTSB1_PAGESHIFT; | |
537 | ||
538 | DBGPE(c_printf("PA 0x%x size 0x%x shift 0x%x nttes 0x%x max 0x%x\n", | |
539 | bypass_pa, size, IOTSB1_PAGESHIFT, nttes, IOTSB1_TSB_SIZE)); | |
540 | ||
541 | ASSERT(nttes <= IOMMU_SIZE(IOTSB1_TSB_SIZE)); | |
542 | ||
543 | ttep = piup->iotsb1; | |
544 | ||
545 | /* | |
546 | * Set up a 4MB mapping for each page in the guests address space | |
547 | */ | |
548 | for (i = 0; i < nttes; i++) { | |
549 | *ttep++ = PIU_IOTTE(bypass_pa + (i << IOTSB1_PAGESHIFT)); | |
550 | } | |
551 | } | |
552 | ||
553 | static void | |
554 | clear_pcie_bypass(int bus_id) | |
555 | { | |
556 | piu_dev_t *piup; | |
557 | ||
558 | /* | |
559 | * Invalidate all entries in the large page IOSTB | |
560 | */ | |
561 | ASSERT(bus_id == 0); | |
562 | piup = (piu_dev_t *)&(piu_dev[bus_id]); | |
563 | c_bzero(piup->iotsb1, IOTSB1_SIZE); | |
564 | } | |
565 | ||
566 | static void | |
567 | config_pcie_bypass(guest_t *guestp, int bus_id) | |
568 | { | |
569 | uint64_t segment_idx; | |
570 | uint64_t bypass_pa; | |
571 | uint64_t bypass_size; | |
572 | ||
573 | DBGPE(c_printf("initializing pcie IO TSB1 RA 0x%x limit 0x%x\n", | |
574 | guestp->real_base, guestp->real_limit)); | |
575 | DBGPE(c_printf("initializing pcie IO TSB1 PA 0x%x len 0x%x\n", | |
576 | guestp->real_base + guestp->mem_offset, | |
577 | guestp->real_limit - guestp->real_base)); | |
578 | ||
579 | segment_idx = guestp->real_base >> RA2PA_SHIFT; | |
580 | ASSERT((guestp->ra2pa_segment[segment_idx].flags & | |
581 | MEM_SEGMENT) == MEM_SEGMENT); | |
582 | ||
583 | /* | |
584 | * Get the base PA of the guests memory segment. The | |
585 | * size of the bypass address space allocated is the | |
586 | * total real memory allocated to the guest. As with HWTW, | |
587 | * this will break if the guests memory has holes. | |
588 | * | |
589 | * Also, do we really need to OR in PIU_IOMMU_BYPASS_BASE, | |
590 | * this will be masked out of the PA used in the IOTSB. | |
591 | */ | |
592 | bypass_pa = (guestp->ra2pa_segment[segment_idx].base + | |
593 | guestp->ra2pa_segment[segment_idx].offset) | | |
594 | PIU_IOMMU_BYPASS_BASE; | |
595 | bypass_size = guestp->real_limit - guestp->real_base; | |
596 | ||
597 | DBGPE(c_printf("PCI Bypass Address 0x%x size 0x%x\n", | |
598 | bypass_pa, bypass_size)); | |
599 | ||
600 | /* | |
601 | * bypass_pa must be aligned on the TSB page size | |
602 | */ | |
603 | if (bypass_pa & IOTSB1_PAGE_MASK) { | |
604 | DBGPE(c_printf("Misaligned Guest PCI Bypass Address 0x%llx \n", | |
605 | bypass_pa)); | |
606 | c_hvabort(-1); | |
607 | } | |
608 | ||
609 | /* | |
610 | * bypass_size must be a multiple of the TSB page size | |
611 | */ | |
612 | if (bypass_size & IOTSB1_PAGE_MASK) { | |
613 | DBGPE(c_printf("Invalid Guest PCI Bypass Size 0x%llx \n", | |
614 | bypass_pa)); | |
615 | c_hvabort(-1); | |
616 | } | |
617 | ||
618 | DBGPE(c_printf("Create IO TSB1 mappings\n")); | |
619 | ||
620 | setup_bypass_mappings(bus_id, bypass_pa, bypass_size); | |
621 | } | |
622 | ||
623 | /* | |
624 | * N2 PIU does not have functionality equivalent to N1 Fire Bypass Mode | |
625 | * but some drivers rely on this so we need to provide a similar | |
626 | * feature for PIU. What we do is allocate a second IOTSB which maps | |
627 | * all of the guests memory using 4MB pages. This imposes the constraint | |
628 | * that the guests address space is 4MB aligned and a multiple of 4MB. | |
629 | * | |
630 | * It would be better to just make the second IOTSB available to the | |
631 | * guest for it to create large page mappings itself. | |
632 | */ | |
633 | void | |
634 | plat_config_pcie_bypass(pcie_device_t *pciep) | |
635 | { | |
636 | int id; | |
637 | ||
638 | id = pciep->id; | |
639 | ASSERT(id == 0); | |
640 | ||
641 | if (pciep->allow_bypass) { | |
642 | DBGPE(c_printf("PCI-E Bypass Mode allowed for guest 0x%x\n", | |
643 | pciep->guestp->guestid)); | |
644 | config_pcie_bypass(pciep->guestp, id); | |
645 | } else { | |
646 | DBGPE(c_printf("PCI-E Bypass not allowed for guest 0x%x\n", | |
647 | pciep->guestp->guestid)); | |
648 | DBGPE(c_printf("invalidating pcie IOTSB1\n")); | |
649 | clear_pcie_bypass(id); | |
650 | } | |
651 | } | |
652 | ||
653 | #endif /* CONFIG_PIU */ |