initial config_read routing

Change-Id: I5d9877e0e05a37e2e719f732e2ff5abc0332af1d
diff --git a/kernel/lib/syscalls/syscalls_ddk_pci.cpp b/kernel/lib/syscalls/syscalls_ddk_pci.cpp
index 9df0d85..059b5d8 100644
--- a/kernel/lib/syscalls/syscalls_ddk_pci.cpp
+++ b/kernel/lib/syscalls/syscalls_ddk_pci.cpp
@@ -330,6 +330,50 @@
     return MX_OK;
 }
 
+
+mx_status_t sys_pci_config_read(mx_handle_t handle, uint16_t offset, size_t width,
+                                user_ptr<uint32_t> out_val) {
+    mxtl::RefPtr<PciDeviceDispatcher> pci_device;
+    mxtl::RefPtr<Dispatcher> dispatcher;
+
+    if (handle == MX_HANDLE_INVALID) {
+        return MX_ERR_BAD_HANDLE;
+    }
+
+    if (out_val.get() == nullptr) {
+        return MX_ERR_INVALID_ARGS;
+    }
+
+    // The only valid reads in the device dependent config region following the header.
+    // For header information callers are expected to use the get_device_info() protocol
+    // method.
+    if (offset < PCIE_STANDARD_CONFIG_HDR_SIZE) {
+        return MX_ERR_OUT_OF_RANGE;
+    }
+
+    // Get the PciDeviceDispatcher from the handle passed in via the pci protocol
+    auto up = ProcessDispatcher::GetCurrent();
+    mx_status_t status = up->GetDispatcherWithRights(handle, MX_RIGHT_READ | MX_RIGHT_WRITE,
+                                                    &pci_device);
+    if (status != MX_OK) {
+        return status;
+    }
+
+    // Based on the width passed in we can use the type safety of the PciConfig layer
+    // to ensure we're getting correctly sized data back and return errors in the PIO
+    // cases.
+    auto config = pci_device->device()->config();
+    switch(width) {
+    case 8u:  return out_val.copy_to_user(static_cast<uint32_t>(config->Read(PciReg8(offset))));
+    case 16u: return out_val.copy_to_user(static_cast<uint32_t>(config->Read(PciReg16(offset))));
+    case 32u: return out_val.copy_to_user(config->Read(PciReg32(offset)));
+    default: return MX_ERR_INVALID_ARGS;
+    }
+
+    // If we reached this point then the width was invalid.
+    return MX_ERR_INVALID_ARGS;
+}
+
 /* This is a transitional method to bootstrap legacy PIO access before
  * PCI moves to userspace.
  */
@@ -713,6 +757,16 @@
     return MX_ERR_NOT_SUPPORTED;
 }
 
+mx_status_t sys_pci_config_read(mx_handle_t handle, uint16_t offset, size_t width,
+                                user_ptr<uint32_t> out_val) {
+    return MX_ERR_NOT_SUPPORTED;
+}
+
+mx_status_t sys_pci_cfg_pio_rw(mx_handle_t handle, uint8_t bus, uint8_t dev, uint8_t func,
+                               uint8_t offset, user_ptr<uint32_t> val, size_t width, bool write) {
+    return MX_ERR_NOT_SUPPORTED;
+}
+
 mx_status_t sys_pci_get_nth_device(mx_handle_t, uint32_t, user_ptr<mx_pcie_device_info_t>,
                                    user_ptr<mx_handle_t>) {
     return MX_ERR_NOT_SUPPORTED;
diff --git a/system/dev/bus/pci/protocol.c b/system/dev/bus/pci/protocol.c
index 3e53480..ee8f474 100644
--- a/system/dev/bus/pci/protocol.c
+++ b/system/dev/bus/pci/protocol.c
@@ -51,6 +51,28 @@
     return status;
 }
 
+
+// These reads are proxied directly over to the device's PciConfig object so the validity of the
+// widths and offsets will be validated on that end and then trickle back to this level of the
+// protocol.
+static mx_status_t pci_config_read(void* ctx, uint16_t offset, size_t width, uint32_t* out_val) {
+    kpci_device_t* device = ctx;
+
+    return mx_pci_config_read(device->handle, offset, width, out_val);
+}
+
+static mx_status_t pci_config_read8(void* ctx, uint16_t offset, uint8_t* out_val) {
+    return pci_config_read(ctx, offset, 8u, (uint32_t*)out_val);
+}
+
+static mx_status_t pci_config_read16(void* ctx, uint16_t offset, uint16_t* out_val) {
+    return pci_config_read(ctx, offset, 16u, (uint32_t*)out_val);
+}
+
+static mx_status_t pci_config_read32(void* ctx, uint16_t offset, uint32_t* out_val) {
+    return pci_config_read(ctx, offset, 32u, out_val);
+}
+
 static mx_status_t pci_get_resource(void* ctx, uint32_t res_id, mx_pci_resource_t* out_res) {
     mx_status_t status = MX_OK;
 
diff --git a/system/public/magenta/syscalls.sysgen b/system/public/magenta/syscalls.sysgen
index 9700023..66e4207 100644
--- a/system/public/magenta/syscalls.sysgen
+++ b/system/public/magenta/syscalls.sysgen
@@ -566,6 +566,10 @@
     (handle: mx_handle_t)
     returns (mx_status_t);
 
+syscall pci_config_read
+    (handle: mx_handle_t, offset: uint16_t, width: size_t, out_val: uint32_t[1] OUT)
+    returns (mx_status_t);
+
 syscall pci_cfg_pio_rw
     (handle: mx_handle_t, bus: uint8_t, dev: uint8_t, func: uint8_t, offset: uint8_t,
         val: uint32_t[1] OUT, width: size_t, write: bool)