1// Copyright 2018 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "platform-protocol-device.h" 6 7#include <stdio.h> 8#include <stdlib.h> 9#include <string.h> 10 11#include <ddk/binding.h> 12#include <ddk/debug.h> 13#include <ddk/device.h> 14#include <ddk/driver.h> 15#include <ddk/metadata.h> 16#include <ddk/protocol/platform-defs.h> 17#include <fbl/function.h> 18#include <zircon/syscalls/resource.h> 19#include <lib/zx/vmar.h> 20#include <lib/zx/vmo.h> 21 22#include "platform-bus.h" 23 24namespace platform_bus { 25 26zx_status_t ProtocolDevice::Create(const pbus_dev_t* pdev, zx_device_t* parent, PlatformBus* bus, 27 fbl::unique_ptr<platform_bus::ProtocolDevice>* out) { 28 fbl::AllocChecker ac; 29 fbl::unique_ptr<platform_bus::ProtocolDevice> dev(new (&ac) 30 platform_bus::ProtocolDevice(parent, bus, pdev)); 31 if (!ac.check()) { 32 return ZX_ERR_NO_MEMORY; 33 } 34 auto status = dev->Init(pdev); 35 if (status != ZX_OK) { 36 return status; 37 } 38 out->swap(dev); 39 return ZX_OK; 40} 41 42ProtocolDevice::ProtocolDevice(zx_device_t* parent, PlatformBus* bus, const pbus_dev_t* pdev) 43 : ProtocolDeviceType(parent), bus_(bus), vid_(pdev->vid), pid_(pdev->pid), 44 did_(pdev->did), resources_(ROOT_DEVICE_ID) { 45 strlcpy(name_, pdev->name, sizeof(name_)); 46} 47 48zx_status_t ProtocolDevice::Init(const pbus_dev_t* pdev) { 49 auto status = resources_.Init(pdev); 50 if (status != ZX_OK) { 51 return status; 52 } 53 54 platform_bus_protocol_t pbus; 55 status = device_get_protocol(parent(), ZX_PROTOCOL_PLATFORM_BUS, &pbus); 56 if (status != ZX_OK) { 57 return status; 58 } 59 60 pbus_ctx_ = pbus.ctx; 61 // Make a copy of the platform bus protocol so we can replace some methods. 62 pbus_ops_ = *pbus.ops; 63 64 // Do not allow calling device_add and protocol_device_add. 65 // Only the board driver should be calling those. 66 pbus_ops_.device_add = [](void* ctx, const pbus_dev_t* dev) { return ZX_ERR_NOT_SUPPORTED; }; 67 pbus_ops_.protocol_device_add = [](void* ctx, uint32_t proto_id, const pbus_dev_t* dev) 68 { return ZX_ERR_NOT_SUPPORTED; }; 69 return ZX_OK; 70} 71 72zx_status_t ProtocolDevice::GetMmio(uint32_t index, pdev_mmio_t* out_mmio) { 73 if (index >= resources_.mmio_count()) { 74 return ZX_ERR_OUT_OF_RANGE; 75 } 76 77 const pbus_mmio_t& mmio = resources_.mmio(index); 78 const zx_paddr_t vmo_base = ROUNDDOWN(mmio.base, PAGE_SIZE); 79 const size_t vmo_size = ROUNDUP(mmio.base + mmio.length - vmo_base, PAGE_SIZE); 80 zx::vmo vmo; 81 82 zx_status_t status = zx_vmo_create_physical(bus_->GetResource(), vmo_base, vmo_size, 83 vmo.reset_and_get_address()); 84 if (status != ZX_OK) { 85 zxlogf(ERROR, "%s: creating vmo failed %d\n", __FUNCTION__, status); 86 return status; 87 } 88 89 char name[32]; 90 snprintf(name, sizeof(name), "mmio %u", index); 91 status = vmo.set_property(ZX_PROP_NAME, name, sizeof(name)); 92 if (status != ZX_OK) { 93 zxlogf(ERROR, "%s: setting vmo name failed %d\n", __FUNCTION__, status); 94 return status; 95 } 96 97 out_mmio->offset = mmio.base - vmo_base; 98 out_mmio->vmo = vmo.release(); 99 out_mmio->size = mmio.length; 100 return ZX_OK; 101} 102 103// TODO(surajmalhotra): Remove after migrating all clients off. 104zx_status_t ProtocolDevice::MapMmio(uint32_t index, uint32_t cache_policy, void** out_vaddr, 105 size_t* out_size, zx_paddr_t* out_paddr, 106 zx_handle_t* out_handle) { 107 if (index >= resources_.mmio_count()) { 108 return ZX_ERR_OUT_OF_RANGE; 109 } 110 111 const pbus_mmio_t& mmio = resources_.mmio(index); 112 const zx_paddr_t vmo_base = ROUNDDOWN(mmio.base, PAGE_SIZE); 113 const size_t vmo_size = ROUNDUP(mmio.base + mmio.length - vmo_base, PAGE_SIZE); 114 zx::vmo vmo; 115 zx_status_t status = zx_vmo_create_physical(bus_->GetResource(), vmo_base, vmo_size, 116 vmo.reset_and_get_address()); 117 if (status != ZX_OK) { 118 zxlogf(ERROR, "platform_dev_map_mmio: zx_vmo_create_physical failed %d\n", status); 119 return status; 120 } 121 122 char name[32]; 123 snprintf(name, sizeof(name), "mmio %u", index); 124 status = vmo.set_property(ZX_PROP_NAME, name, sizeof(name)); 125 if (status != ZX_OK) { 126 zxlogf(ERROR, "%s: setting vmo name failed %d\n", __FUNCTION__, status); 127 return status; 128 } 129 130 status = vmo.set_cache_policy(cache_policy); 131 if (status != ZX_OK) { 132 zxlogf(ERROR, "platform_dev_map_mmio: zx_vmo_set_cache_policy failed %d\n", status); 133 return status; 134 } 135 136 uintptr_t virt; 137 status = zx::vmar::root_self()->map(0, vmo, 0, vmo_size, ZX_VM_PERM_READ | 138 ZX_VM_PERM_WRITE | ZX_VM_MAP_RANGE, &virt); 139 if (status != ZX_OK) { 140 zxlogf(ERROR, "platform_dev_map_mmio: zx_vmar_map failed %d\n", status); 141 return status; 142 } 143 144 *out_size = mmio.length; 145 *out_handle = vmo.release(); 146 if (out_paddr) { 147 *out_paddr = vmo_base; 148 } 149 *out_vaddr = reinterpret_cast<void*>(virt + (mmio.base - vmo_base)); 150 return ZX_OK; 151} 152 153zx_status_t ProtocolDevice::MapInterrupt(uint32_t index, uint32_t flags, zx_handle_t* out_handle) { 154 if (index >= resources_.irq_count()) { 155 return ZX_ERR_OUT_OF_RANGE; 156 } 157 if (out_handle == nullptr) { 158 return ZX_ERR_INVALID_ARGS; 159 } 160 161 const pbus_irq_t& irq = resources_.irq(index); 162 if (flags == 0) { 163 flags = irq.mode; 164 } 165 zx_status_t status = zx_interrupt_create(bus_->GetResource(), irq.irq, flags, out_handle); 166 if (status != ZX_OK) { 167 zxlogf(ERROR, "platform_dev_map_interrupt: zx_interrupt_create failed %d\n", status); 168 return status; 169 } 170 return status; 171} 172 173zx_status_t ProtocolDevice::GetBti(uint32_t index, zx_handle_t* out_handle) { 174 if (index >= resources_.bti_count()) { 175 return ZX_ERR_OUT_OF_RANGE; 176 } 177 if (out_handle == nullptr) { 178 return ZX_ERR_INVALID_ARGS; 179 } 180 181 const pbus_bti_t& bti = resources_.bti(index); 182 183 return bus_->GetBti(bti.iommu_index, bti.bti_id, out_handle); 184} 185 186zx_status_t ProtocolDevice::GetDeviceInfo(pdev_device_info_t* out_info) { 187 pdev_device_info_t info = { 188 .vid = vid_, 189 .pid = pid_, 190 .did = did_, 191 .mmio_count = static_cast<uint32_t>(resources_.mmio_count()), 192 .irq_count = static_cast<uint32_t>(resources_.irq_count()), 193 .gpio_count = static_cast<uint32_t>(resources_.gpio_count()), 194 .i2c_channel_count = static_cast<uint32_t>(resources_.i2c_channel_count()), 195 .clk_count = static_cast<uint32_t>(resources_.clk_count()), 196 .bti_count = static_cast<uint32_t>(resources_.bti_count()), 197 .metadata_count = static_cast<uint32_t>(resources_.metadata_count()), 198 .reserved = {}, 199 .name = {}, 200 }; 201 static_assert(sizeof(info.name) == sizeof(name_), ""); 202 memcpy(info.name, name_, sizeof(out_info->name)); 203 memcpy(out_info, &info, sizeof(info)); 204 205 return ZX_OK; 206} 207 208zx_status_t ProtocolDevice::GetBoardInfo(pdev_board_info_t* out_info) { 209 return bus_->GetBoardInfo(out_info); 210} 211 212zx_status_t ProtocolDevice::DeviceAdd(uint32_t index, device_add_args_t* args, zx_device_t** out) { 213 return ZX_ERR_NOT_SUPPORTED; 214} 215 216zx_status_t ProtocolDevice::GetProtocol(uint32_t proto_id, uint32_t index, void* out_protocol) { 217 // Pass through to DdkGetProtocol if index is zero 218 if (index != 0) { 219 return ZX_ERR_OUT_OF_RANGE; 220 } 221 return DdkGetProtocol(proto_id, out_protocol); 222} 223 224zx_status_t ProtocolDevice::DdkGetProtocol(uint32_t proto_id, void* out) { 225 if (proto_id == ZX_PROTOCOL_PLATFORM_DEV) { 226 auto proto = static_cast<platform_device_protocol_t*>(out); 227 proto->ops = &pdev_proto_ops_; 228 proto->ctx = this; 229 return ZX_OK; 230 } else if (proto_id == ZX_PROTOCOL_PLATFORM_BUS) { 231 // Protocol implementation drivers get a restricted subset of the platform bus protocol 232 auto proto = static_cast<platform_bus_protocol_t*>(out); 233 proto->ops = &pbus_ops_; 234 proto->ctx = pbus_ctx_; 235 return ZX_OK; 236 } else { 237 return bus_->DdkGetProtocol(proto_id, out); 238 } 239} 240 241void ProtocolDevice::DdkRelease() { 242 delete this; 243} 244 245zx_status_t ProtocolDevice::Start() { 246 zx_device_prop_t props[] = { 247 {BIND_PLATFORM_DEV_VID, 0, vid_}, 248 {BIND_PLATFORM_DEV_PID, 0, pid_}, 249 {BIND_PLATFORM_DEV_DID, 0, did_}, 250 }; 251 252 char name[ZX_DEVICE_NAME_MAX]; 253 if (vid_ == PDEV_VID_GENERIC && pid_ == PDEV_PID_GENERIC && did_ == PDEV_DID_KPCI) { 254 strlcpy(name, "pci", sizeof(name)); 255 } else { 256 snprintf(name, sizeof(name), "%02x:%02x:%01x", vid_, pid_, did_); 257 } 258 259 // Protocol devices run in our devhost. 260 uint32_t device_add_flags = 0; 261 262 const size_t metadata_count = resources_.metadata_count(); 263 const size_t boot_metadata_count = resources_.boot_metadata_count(); 264 if (metadata_count > 0 || boot_metadata_count > 0) { 265 // Keep device invisible until after we add its metadata. 266 device_add_flags |= DEVICE_ADD_INVISIBLE; 267 } 268 269 auto status = DdkAdd(name, device_add_flags, props, fbl::count_of(props)); 270 if (status != ZX_OK) { 271 return status; 272 } 273 274 if (metadata_count > 0 || boot_metadata_count > 0) { 275 for (size_t i = 0; i < metadata_count; i++) { 276 const auto& metadata = resources_.metadata(i); 277 status = DdkAddMetadata(metadata.type, metadata.data, metadata.len); 278 if (status != ZX_OK) { 279 DdkRemove(); 280 return status; 281 } 282 } 283 284 for (size_t i = 0; i < boot_metadata_count; i++) { 285 const auto& metadata = resources_.boot_metadata(i); 286 const void* data; 287 uint32_t length; 288 status = bus_->GetZbiMetadata(metadata.zbi_type, metadata.zbi_extra, &data, &length); 289 if (status == ZX_OK) { 290 status = DdkAddMetadata(metadata.zbi_type, data, length); 291 } 292 if (status != ZX_OK) { 293 DdkRemove(); 294 return status; 295 } 296 } 297 298 DdkMakeVisible(); 299 } 300 301 return ZX_OK; 302} 303 304} // namespace platform_bus 305