1// Copyright 2016 The Fuchsia Authors 2// 3// Use of this source code is governed by a MIT-style 4// license that can be found in the LICENSE file or at 5// https://opensource.org/licenses/MIT 6 7#include <err.h> 8#include <platform.h> 9#include <stdint.h> 10#include <stdio.h> 11#include <stdlib.h> 12#include <string.h> 13#include <trace.h> 14 15#include <dev/interrupt.h> 16#include <lib/pci/pio.h> 17#include <lib/user_copy/user_ptr.h> 18#include <object/handle.h> 19#include <object/process_dispatcher.h> 20#include <object/resource.h> 21#include <object/vm_object_dispatcher.h> 22#include <vm/vm_object_physical.h> 23 24#include <fbl/algorithm.h> 25#include <fbl/alloc_checker.h> 26#include <fbl/limits.h> 27#include <fbl/ref_ptr.h> 28#include <fbl/unique_free_ptr.h> 29#include <zircon/syscalls/pci.h> 30 31#include "priv.h" 32 33#define LOCAL_TRACE 0 34 35// If we were built with the GFX console, make sure that it is un-bound when 36// user mode takes control of PCI. Note: there should probably be a cleaner way 37// of doing this. Not all system have PCI, and (eventually) not all systems 38// will attempt to initialize PCI. Someday, there should be a different way of 39// handing off from early/BSOD kernel mode graphics to user mode. 40#include <lib/gfxconsole.h> 41static inline void shutdown_early_init_console() { 42 gfxconsole_bind_display(nullptr, nullptr); 43} 44 45#ifdef WITH_KERNEL_PCIE 46#include <dev/pcie_bus_driver.h> 47#include <dev/pcie_root.h> 48#include <object/pci_device_dispatcher.h> 49 50// Implementation of a PcieRoot with a look-up table based legacy IRQ swizzler 51// suitable for use with ACPI style swizzle definitions. 52class PcieRootLUTSwizzle : public PcieRoot { 53public: 54 static fbl::RefPtr<PcieRoot> Create(PcieBusDriver& bus_drv, 55 uint managed_bus_id, 56 const zx_pci_irq_swizzle_lut_t& lut) { 57 fbl::AllocChecker ac; 58 auto root = fbl::AdoptRef(new (&ac) PcieRootLUTSwizzle(bus_drv, 59 managed_bus_id, 60 lut)); 61 if (!ac.check()) { 62 TRACEF("Out of memory attemping to create PCIe root to manage bus ID 0x%02x\n", 63 managed_bus_id); 64 return nullptr; 65 } 66 67 return root; 68 } 69 70 zx_status_t Swizzle(uint dev_id, uint func_id, uint pin, uint* irq) override { 71 if ((irq == nullptr) || 72 (dev_id >= fbl::count_of(lut_)) || 73 (func_id >= fbl::count_of(lut_[dev_id])) || 74 (pin >= fbl::count_of(lut_[dev_id][func_id]))) 75 return ZX_ERR_INVALID_ARGS; 76 77 *irq = lut_[dev_id][func_id][pin]; 78 return (*irq == ZX_PCI_NO_IRQ_MAPPING) ? ZX_ERR_NOT_FOUND : ZX_OK; 79 } 80 81private: 82 PcieRootLUTSwizzle(PcieBusDriver& bus_drv, 83 uint managed_bus_id, 84 const zx_pci_irq_swizzle_lut_t& lut) 85 : PcieRoot(bus_drv, managed_bus_id) { 86 ::memcpy(&lut_, &lut, sizeof(lut_)); 87 } 88 89 zx_pci_irq_swizzle_lut_t lut_; 90}; 91 92// Scan |lut| for entries mapping to |irq|, and replace them with 93// ZX_PCI_NO_IRQ_MAPPING. 94static void pci_irq_swizzle_lut_remove_irq(zx_pci_irq_swizzle_lut_t* lut, uint32_t irq) { 95 for (size_t dev = 0; dev < fbl::count_of(*lut); ++dev) { 96 for (size_t func = 0; func < fbl::count_of((*lut)[dev]); ++func) { 97 for (size_t pin = 0; pin < fbl::count_of((*lut)[dev][func]); ++pin) { 98 uint32_t* assigned_irq = &(*lut)[dev][func][pin]; 99 if (*assigned_irq == irq) { 100 *assigned_irq = ZX_PCI_NO_IRQ_MAPPING; 101 } 102 } 103 } 104 } 105} 106 107zx_status_t sys_pci_add_subtract_io_range(zx_handle_t handle, bool mmio, uint64_t base, uint64_t len, bool add) { 108 109 LTRACEF("handle %x mmio %d base %#" PRIx64 " len %#" PRIx64 " add %d\n", handle, mmio, base, len, add); 110 111 // TODO(ZX-971): finer grained validation 112 // TODO(security): Add additional access checks 113 zx_status_t status; 114 if ((status = validate_resource(handle, ZX_RSRC_KIND_ROOT)) < 0) { 115 return status; 116 } 117 118 auto pcie = PcieBusDriver::GetDriver(); 119 if (pcie == nullptr) { 120 return ZX_ERR_BAD_STATE; 121 } 122 123 PciAddrSpace addr_space = mmio ? PciAddrSpace::MMIO : PciAddrSpace::PIO; 124 125 if (add) { 126 return pcie->AddBusRegion(base, len, addr_space); 127 } else { 128 return pcie->SubtractBusRegion(base, len, addr_space); 129 } 130} 131 132zx_status_t sys_pci_init(zx_handle_t handle, user_in_ptr<const zx_pci_init_arg_t> _init_buf, uint32_t len) { 133 // TODO(ZX-971): finer grained validation 134 // TODO(security): Add additional access checks 135 zx_status_t status; 136 if ((status = validate_resource(handle, ZX_RSRC_KIND_ROOT)) < 0) { 137 return status; 138 } 139 140 fbl::unique_free_ptr<zx_pci_init_arg_t> arg; 141 142 if (len < sizeof(*arg) || len > ZX_PCI_INIT_ARG_MAX_SIZE) { 143 return ZX_ERR_INVALID_ARGS; 144 } 145 146 auto pcie = PcieBusDriver::GetDriver(); 147 if (pcie == nullptr) 148 return ZX_ERR_BAD_STATE; 149 150 // we have to malloc instead of new since this is a variable-sized structure 151 arg.reset(static_cast<zx_pci_init_arg_t*>(malloc(len))); 152 if (!arg) { 153 return ZX_ERR_NO_MEMORY; 154 } 155 { 156 status = _init_buf.reinterpret<const void>().copy_array_from_user( 157 arg.get(), len); 158 if (status != ZX_OK) { 159 return status; 160 } 161 } 162 163 if (LOCAL_TRACE) { 164 TRACEF("%u address window%s found in init arg\n", arg->addr_window_count, 165 (arg->addr_window_count == 1) ? "" : "s"); 166 for (uint32_t i = 0; i < arg->addr_window_count; i++) { 167 printf("[%u]\n\tis_mmio: %d\n\thas_ecam: %d\n\tbase: %#" PRIxPTR "\n" 168 "\tsize: %zu\n\tbus_start: %u\n\tbus_end: %u\n", 169 i, 170 arg->addr_windows[i].is_mmio, arg->addr_windows[i].has_ecam, 171 arg->addr_windows[i].base, arg->addr_windows[i].size, 172 arg->addr_windows[i].bus_start, arg->addr_windows[i].bus_end); 173 } 174 } 175 176 const uint32_t win_count = arg->addr_window_count; 177 if (len != sizeof(*arg) + sizeof(arg->addr_windows[0]) * win_count) { 178 return ZX_ERR_INVALID_ARGS; 179 } 180 181 if (arg->num_irqs > fbl::count_of(arg->irqs)) { 182 return ZX_ERR_INVALID_ARGS; 183 } 184 185 // Configure interrupts 186 for (unsigned int i = 0; i < arg->num_irqs; ++i) { 187 uint32_t irq = arg->irqs[i].global_irq; 188 if (!is_valid_interrupt(irq, 0)) { 189 // If the interrupt isn't valid, mask it out of the IRQ swizzle table 190 // and don't activate it. Attempts to use legacy IRQs for the device 191 // will fail later. 192 arg->irqs[i].global_irq = ZX_PCI_NO_IRQ_MAPPING; 193 pci_irq_swizzle_lut_remove_irq(&arg->dev_pin_to_global_irq, irq); 194 continue; 195 } 196 197 enum interrupt_trigger_mode tm = IRQ_TRIGGER_MODE_EDGE; 198 if (arg->irqs[i].level_triggered) { 199 tm = IRQ_TRIGGER_MODE_LEVEL; 200 } 201 enum interrupt_polarity pol = IRQ_POLARITY_ACTIVE_LOW; 202 if (arg->irqs[i].active_high) { 203 pol = IRQ_POLARITY_ACTIVE_HIGH; 204 } 205 206 zx_status_t status = configure_interrupt(irq, tm, pol); 207 if (status != ZX_OK) { 208 return status; 209 } 210 } 211 // TODO(teisenbe): For now assume there is only one ECAM 212 if (win_count != 1) { 213 return ZX_ERR_INVALID_ARGS; 214 } 215 216 if (arg->addr_windows[0].bus_start != 0) { 217 return ZX_ERR_INVALID_ARGS; 218 } 219 220 if (arg->addr_windows[0].bus_start > arg->addr_windows[0].bus_end) { 221 return ZX_ERR_INVALID_ARGS; 222 } 223 224#if ARCH_X86 225 // Check for a quirk that we've seen. Some systems will report overly large 226 // PCIe config regions that collide with architectural registers. 227 unsigned int num_buses = arg->addr_windows[0].bus_end - 228 arg->addr_windows[0].bus_start + 1; 229 paddr_t end = arg->addr_windows[0].base + 230 num_buses * PCIE_ECAM_BYTE_PER_BUS; 231 const paddr_t high_limit = 0xfec00000ULL; 232 if (end > high_limit) { 233 TRACEF("PCIe config space collides with arch devices, truncating\n"); 234 end = high_limit; 235 if (end < arg->addr_windows[0].base) { 236 return ZX_ERR_INVALID_ARGS; 237 } 238 arg->addr_windows[0].size = ROUNDDOWN(end - arg->addr_windows[0].base, 239 PCIE_ECAM_BYTE_PER_BUS); 240 uint64_t new_bus_end = (arg->addr_windows[0].size / PCIE_ECAM_BYTE_PER_BUS) + 241 arg->addr_windows[0].bus_start - 1; 242 if (new_bus_end >= PCIE_MAX_BUSSES) { 243 return ZX_ERR_INVALID_ARGS; 244 } 245 arg->addr_windows[0].bus_end = static_cast<uint8_t>(new_bus_end); 246 } 247#endif 248 249 if (arg->addr_windows[0].is_mmio) { 250 if (arg->addr_windows[0].size < PCIE_ECAM_BYTE_PER_BUS) { 251 return ZX_ERR_INVALID_ARGS; 252 } 253 if (arg->addr_windows[0].size / PCIE_ECAM_BYTE_PER_BUS > 254 PCIE_MAX_BUSSES - arg->addr_windows[0].bus_start) { 255 256 return ZX_ERR_INVALID_ARGS; 257 } 258 259 // TODO(johngro): Update the syscall to pass a paddr_t for base instead of a uint64_t 260 ASSERT(arg->addr_windows[0].base < fbl::numeric_limits<paddr_t>::max()); 261 262 // TODO(johngro): Do not limit this to a single range. Instead, fetch all 263 // of the ECAM ranges from ACPI, as well as the appropriate bus start/end 264 // ranges. 265 const PcieBusDriver::EcamRegion ecam = { 266 .phys_base = static_cast<paddr_t>(arg->addr_windows[0].base), 267 .size = arg->addr_windows[0].size, 268 .bus_start = 0x00, 269 .bus_end = static_cast<uint8_t>((arg->addr_windows[0].size / PCIE_ECAM_BYTE_PER_BUS) - 1), 270 }; 271 272 zx_status_t ret = pcie->AddEcamRegion(ecam); 273 if (ret != ZX_OK) { 274 TRACEF("Failed to add ECAM region to PCIe bus driver! (ret %d)\n", ret); 275 return ret; 276 } 277 } 278 279 // TODO(johngro): Change the user-mode and devmgr behavior to add all of the 280 // roots in the system. Do not assume that there is a single root, nor that 281 // it manages bus ID 0. 282 auto root = PcieRootLUTSwizzle::Create(*pcie, 0, arg->dev_pin_to_global_irq); 283 if (root == nullptr) 284 return ZX_ERR_NO_MEMORY; 285 286 // Enable PIO config space if the address window was not MMIO 287 pcie->EnablePIOWorkaround(!arg->addr_windows[0].is_mmio); 288 289 zx_status_t ret = pcie->AddRoot(fbl::move(root)); 290 if (ret != ZX_OK) { 291 TRACEF("Failed to add root complex to PCIe bus driver! (ret %d)\n", ret); 292 return ret; 293 } 294 295 ret = pcie->StartBusDriver(); 296 if (ret != ZX_OK) { 297 TRACEF("Failed to start PCIe bus driver! (ret %d)\n", ret); 298 return ret; 299 } 300 301 shutdown_early_init_console(); 302 return ZX_OK; 303} 304 305zx_status_t sys_pci_get_nth_device(zx_handle_t hrsrc, 306 uint32_t index, 307 user_out_ptr<zx_pcie_device_info_t> out_info, 308 user_out_handle* out_handle) { 309 /** 310 * Returns the pci config of a device. 311 * @param index Device index 312 * @param out_info Device info (BDF address, vendor id, etc...) 313 */ 314 LTRACEF("handle %x index %u\n", hrsrc, index); 315 316 // TODO(ZX-971): finer grained validation 317 zx_status_t status; 318 if ((status = validate_resource(hrsrc, ZX_RSRC_KIND_ROOT)) < 0) { 319 return status; 320 } 321 322 if (!out_info) { 323 return ZX_ERR_INVALID_ARGS; 324 } 325 326 fbl::RefPtr<Dispatcher> dispatcher; 327 zx_rights_t rights; 328 zx_pcie_device_info_t info; 329 status = PciDeviceDispatcher::Create(index, &info, &dispatcher, &rights); 330 if (status != ZX_OK) { 331 return status; 332 } 333 334 // If everything is successful add the handle to the process 335 status = out_info.copy_to_user(info); 336 if (status != ZX_OK) 337 return status; 338 339 return out_handle->make(fbl::move(dispatcher), rights); 340} 341 342zx_status_t sys_pci_config_read(zx_handle_t handle, uint16_t offset, size_t width, 343 user_out_ptr<uint32_t> out_val) { 344 fbl::RefPtr<PciDeviceDispatcher> pci_device; 345 fbl::RefPtr<Dispatcher> dispatcher; 346 347 // Get the PciDeviceDispatcher from the handle passed in via the pci protocol 348 auto up = ProcessDispatcher::GetCurrent(); 349 zx_status_t status = up->GetDispatcherWithRights(handle, ZX_RIGHT_READ | ZX_RIGHT_WRITE, 350 &pci_device); 351 if (status != ZX_OK) { 352 return status; 353 } 354 355 auto device = pci_device->device(); 356 auto cfg_size = device->is_pcie() ? PCIE_EXTENDED_CONFIG_SIZE : PCIE_BASE_CONFIG_SIZE; 357 if (out_val.get() == nullptr || offset + width > cfg_size) { 358 return ZX_ERR_INVALID_ARGS; 359 } 360 361 // Based on the width passed in we can use the type safety of the PciConfig layer 362 // to ensure we're getting correctly sized data back and return errors in the PIO 363 // cases. 364 auto config = device->config(); 365 switch (width) { 366 case 1u: 367 return out_val.copy_to_user(static_cast<uint32_t>(config->Read(PciReg8(offset)))); 368 case 2u: 369 return out_val.copy_to_user(static_cast<uint32_t>(config->Read(PciReg16(offset)))); 370 case 4u: 371 return out_val.copy_to_user(config->Read(PciReg32(offset))); 372 } 373 374 // If we reached this point then the width was invalid. 375 return ZX_ERR_INVALID_ARGS; 376} 377 378zx_status_t sys_pci_config_write(zx_handle_t handle, uint16_t offset, size_t width, uint32_t val) { 379 fbl::RefPtr<PciDeviceDispatcher> pci_device; 380 fbl::RefPtr<Dispatcher> dispatcher; 381 382 // Get the PciDeviceDispatcher from the handle passed in via the pci protocol 383 auto up = ProcessDispatcher::GetCurrent(); 384 zx_status_t status = up->GetDispatcherWithRights(handle, ZX_RIGHT_READ | ZX_RIGHT_WRITE, 385 &pci_device); 386 if (status != ZX_OK) { 387 return status; 388 } 389 390 // Writes to the PCI header or outside of the device's config space are not allowed. 391 auto device = pci_device->device(); 392 auto cfg_size = device->is_pcie() ? PCIE_EXTENDED_CONFIG_SIZE : PCIE_BASE_CONFIG_SIZE; 393 if (offset < ZX_PCI_STANDARD_CONFIG_HDR_SIZE || offset + width > cfg_size) { 394 return ZX_ERR_INVALID_ARGS; 395 } 396 397 auto config = device->config(); 398 switch (width) { 399 case 1u: 400 config->Write(PciReg8(offset), static_cast<uint8_t>(val & UINT8_MAX)); 401 break; 402 case 2u: 403 config->Write(PciReg16(offset), static_cast<uint16_t>(val & UINT16_MAX)); 404 break; 405 case 4u: 406 config->Write(PciReg32(offset), val); 407 break; 408 default: 409 return ZX_ERR_INVALID_ARGS; 410 } 411 412 return ZX_OK; 413} 414/* This is a transitional method to bootstrap legacy PIO access before 415 * PCI moves to userspace. 416 */ 417zx_status_t sys_pci_cfg_pio_rw(zx_handle_t handle, uint8_t bus, uint8_t dev, uint8_t func, 418 uint8_t offset, user_inout_ptr<uint32_t> val, size_t width, bool write) { 419#if ARCH_X86 420 uint32_t val_; 421 zx_status_t status = validate_resource(handle, ZX_RSRC_KIND_ROOT); 422 if (status != ZX_OK) { 423 return status; 424 } 425 426 if (write) { 427 val.copy_from_user(&val_); 428 status = Pci::PioCfgWrite(bus, dev, func, offset, val_, width); 429 } else { 430 status = Pci::PioCfgRead(bus, dev, func, offset, &val_, width); 431 if (status == ZX_OK) { 432 val.copy_to_user(val_); 433 } 434 } 435 436 return status; 437#else 438 return ZX_ERR_NOT_SUPPORTED; 439#endif 440} 441 442zx_status_t sys_pci_enable_bus_master(zx_handle_t dev_handle, bool enable) { 443 /** 444 * Enables or disables bus mastering for the PCI device associated with the handle. 445 * @param handle Handle associated with a PCI device 446 * @param enable true if bus mastering should be enabled. 447 */ 448 LTRACEF("handle %x\n", dev_handle); 449 450 auto up = ProcessDispatcher::GetCurrent(); 451 452 fbl::RefPtr<PciDeviceDispatcher> pci_device; 453 zx_status_t status = up->GetDispatcherWithRights(dev_handle, ZX_RIGHT_WRITE, &pci_device); 454 if (status != ZX_OK) 455 return status; 456 457 return pci_device->EnableBusMaster(enable); 458} 459 460zx_status_t sys_pci_reset_device(zx_handle_t dev_handle) { 461 /** 462 * Resets the PCI device associated with the handle. 463 * @param handle Handle associated with a PCI device 464 */ 465 LTRACEF("handle %x\n", dev_handle); 466 467 auto up = ProcessDispatcher::GetCurrent(); 468 469 fbl::RefPtr<PciDeviceDispatcher> pci_device; 470 zx_status_t status = up->GetDispatcherWithRights(dev_handle, ZX_RIGHT_WRITE, &pci_device); 471 if (status != ZX_OK) 472 return status; 473 474 return pci_device->ResetDevice(); 475} 476 477zx_status_t sys_pci_get_bar(zx_handle_t dev_handle, 478 uint32_t bar_num, 479 user_out_ptr<zx_pci_bar_t> out_bar, 480 user_out_handle* out_handle) { 481 if (dev_handle == ZX_HANDLE_INVALID || 482 !out_bar || 483 bar_num >= PCIE_MAX_BAR_REGS) { 484 return ZX_ERR_INVALID_ARGS; 485 } 486 487 // Grab the PCI device object 488 auto up = ProcessDispatcher::GetCurrent(); 489 fbl::RefPtr<PciDeviceDispatcher> pci_device; 490 zx_status_t status = up->GetDispatcherWithRights(dev_handle, 491 ZX_RIGHT_READ | ZX_RIGHT_WRITE, &pci_device); 492 if (status != ZX_OK) { 493 return status; 494 } 495 496 // Get bar info from the device via the dispatcher and make sure it makes sense 497 const pcie_bar_info_t* info = pci_device->GetBar(bar_num); 498 if (info == nullptr || info->size == 0) { 499 return ZX_ERR_NOT_FOUND; 500 } 501 502 // A bar can be MMIO, PIO, or unused. In the MMIO case it can be passed 503 // back to the caller as a VMO. 504 zx_pci_bar_t bar = {}; 505 bar.size = info->size; 506 bar.type = (info->is_mmio) ? ZX_PCI_BAR_TYPE_MMIO : ZX_PCI_BAR_TYPE_PIO; 507 508 // MMIO based bars are passed back using a VMO. If we end up creating one here 509 // without errors then later a handle will be passed back to the caller. 510 fbl::RefPtr<Dispatcher> dispatcher; 511 fbl::RefPtr<VmObject> vmo; 512 zx_rights_t rights; 513 if (info->is_mmio) { 514 // Create a VMO mapping to the address / size of the mmio region this bar 515 // was allocated at 516 status = VmObjectPhysical::Create(info->bus_addr, 517 fbl::max<uint64_t>(info->size, PAGE_SIZE), &vmo); 518 if (status != ZX_OK) { 519 return status; 520 } 521 522 // Set the name of the vmo for tracking 523 char name[32]; 524 auto dev = pci_device->device(); 525 snprintf(name, sizeof(name), "pci-%02x:%02x.%1x-bar%u", 526 dev->bus_id(), dev->dev_id(), dev->func_id(), bar_num); 527 vmo->set_name(name, sizeof(name)); 528 529 // Now that the vmo has been created for the bar, create a handle to 530 // the appropriate dispatcher for the caller 531 status = VmObjectDispatcher::Create(vmo, &dispatcher, &rights); 532 if (status != ZX_OK) { 533 return status; 534 } 535 536 pci_device->EnableMmio(true); 537 } else { 538 DEBUG_ASSERT(info->bus_addr != 0); 539 bar.addr = info->bus_addr; 540 pci_device->EnablePio(true); 541 } 542 543 // Metadata has been sorted out, so copy back the structure to userspace 544 // and then account for the vmo handle if one was created. 545 status = out_bar.copy_to_user(bar); 546 if (status != ZX_OK) { 547 return status; 548 } 549 550 if (vmo) { 551 return out_handle->make(fbl::move(dispatcher), rights); 552 } 553 554 return ZX_OK; 555} 556 557zx_status_t sys_pci_map_interrupt(zx_handle_t dev_handle, 558 int32_t which_irq, 559 user_out_handle* out_handle) { 560 /** 561 * Returns a handle that can be waited on. 562 * @param handle Handle associated with a PCI device 563 * @param which_irq Identifier for an IRQ, returned in sys_pci_get_nth_device, or -1 for legacy 564 * interrupts 565 * @param out_handle pointer to a handle to associate with the interrupt mapping 566 */ 567 LTRACEF("handle %x\n", dev_handle); 568 569 auto up = ProcessDispatcher::GetCurrent(); 570 571 fbl::RefPtr<PciDeviceDispatcher> pci_device; 572 zx_status_t status = 573 up->GetDispatcherWithRights(dev_handle, ZX_RIGHT_READ, &pci_device); 574 if (status != ZX_OK) 575 return status; 576 577 fbl::RefPtr<Dispatcher> interrupt_dispatcher; 578 zx_rights_t rights; 579 zx_status_t result = pci_device->MapInterrupt(which_irq, &interrupt_dispatcher, &rights); 580 if (result != ZX_OK) 581 return result; 582 583 return out_handle->make(fbl::move(interrupt_dispatcher), rights); 584} 585 586/** 587 * Gets info about the capabilities of a PCI device's IRQ modes. 588 * @param handle Handle associated with a PCI device. 589 * @param mode The IRQ mode whose capabilities are to be queried. 590 * @param out_len Out param which will hold the maximum number of IRQs supported by the mode. 591 */ 592zx_status_t sys_pci_query_irq_mode(zx_handle_t dev_handle, 593 uint32_t mode, 594 user_out_ptr<uint32_t> out_max_irqs) { 595 LTRACEF("handle %x\n", dev_handle); 596 597 auto up = ProcessDispatcher::GetCurrent(); 598 599 fbl::RefPtr<PciDeviceDispatcher> pci_device; 600 zx_status_t status = up->GetDispatcherWithRights(dev_handle, ZX_RIGHT_READ, &pci_device); 601 if (status != ZX_OK) 602 return status; 603 604 uint32_t max_irqs; 605 zx_status_t result = pci_device->QueryIrqModeCaps((zx_pci_irq_mode_t)mode, &max_irqs); 606 if (result != ZX_OK) 607 return result; 608 609 status = out_max_irqs.copy_to_user(max_irqs); 610 if (status != ZX_OK) 611 return status; 612 613 return result; 614} 615 616/** 617 * Selects an IRQ mode for a PCI device. 618 * @param handle Handle associated with a PCI device. 619 * @param mode The IRQ mode to select. 620 * @param requested_irq_count The number of IRQs to select request for the given mode. 621 */ 622zx_status_t sys_pci_set_irq_mode(zx_handle_t dev_handle, 623 uint32_t mode, 624 uint32_t requested_irq_count) { 625 LTRACEF("handle %x\n", dev_handle); 626 627 auto up = ProcessDispatcher::GetCurrent(); 628 629 fbl::RefPtr<PciDeviceDispatcher> pci_device; 630 zx_status_t status = up->GetDispatcherWithRights(dev_handle, ZX_RIGHT_WRITE, &pci_device); 631 if (status != ZX_OK) 632 return status; 633 634 return pci_device->SetIrqMode((zx_pci_irq_mode_t)mode, requested_irq_count); 635} 636#else // WITH_KERNEL_PCIE 637zx_status_t sys_pci_init(zx_handle_t, user_in_ptr<const zx_pci_init_arg_t>, uint32_t) { 638 shutdown_early_init_console(); 639 return ZX_OK; 640} 641 642zx_status_t sys_pci_add_subtract_io_range(zx_handle_t handle, bool mmio, uint64_t base, uint64_t len, bool add) { 643 return ZX_ERR_NOT_SUPPORTED; 644} 645 646zx_status_t sys_pci_config_read(zx_handle_t handle, uint16_t offset, size_t width, 647 user_out_ptr<uint32_t> out_val) { 648 return ZX_ERR_NOT_SUPPORTED; 649} 650 651zx_status_t sys_pci_config_write(zx_handle_t handle, uint16_t offset, size_t width, uint32_t val) { 652 return ZX_ERR_NOT_SUPPORTED; 653} 654 655zx_status_t sys_pci_cfg_pio_rw(zx_handle_t handle, uint8_t bus, uint8_t dev, uint8_t func, 656 uint8_t offset, user_inout_ptr<uint32_t> val, size_t width, bool write) { 657 return ZX_ERR_NOT_SUPPORTED; 658} 659 660zx_status_t sys_pci_get_nth_device(zx_handle_t, uint32_t, user_inout_ptr<zx_pcie_device_info_t>, 661 user_out_handle*) { 662 return ZX_ERR_NOT_SUPPORTED; 663} 664 665zx_status_t sys_pci_enable_bus_master(zx_handle_t, bool) { 666 return ZX_ERR_NOT_SUPPORTED; 667} 668 669zx_status_t sys_pci_reset_device(zx_handle_t) { 670 return ZX_ERR_NOT_SUPPORTED; 671} 672 673zx_status_t sys_pci_get_nth_device(zx_handle_t hrsrc, 674 uint32_t index, 675 user_out_ptr<zx_pcie_device_info_t> out_info, 676 user_out_handle* out_handle) { 677 return ZX_ERR_NOT_SUPPORTED; 678} 679 680zx_status_t sys_pci_get_bar(zx_handle_t dev_handle, 681 uint32_t bar_num, 682 user_out_ptr<zx_pci_bar_t> out_bar, 683 user_out_handle* out_handle) { 684 return ZX_ERR_NOT_SUPPORTED; 685} 686 687zx_status_t sys_pci_map_interrupt(zx_handle_t, int32_t, user_out_handle*) { 688 return ZX_ERR_NOT_SUPPORTED; 689} 690 691zx_status_t sys_pci_query_irq_mode(zx_handle_t, uint32_t, user_out_ptr<uint32_t>) { 692 return ZX_ERR_NOT_SUPPORTED; 693} 694 695zx_status_t sys_pci_set_irq_mode(zx_handle_t, uint32_t, uint32_t) { 696 return ZX_ERR_NOT_SUPPORTED; 697} 698#endif // WITH_KERNEL_PCIE 699