blob: 99b80e02bdcb609d95b933cab3a27134dfe23fb4 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "pinned-buffer.h"
#include <climits>
#include <utility>
fbl::RefPtr<PinnedBuffer> PinnedBuffer::Create(size_t size, const zx::bti& bti,
uint32_t cache_policy) {
fbl::RefPtr<fzl::VmarManager> vmar_mgr;
if (!bti.is_valid() || (size & (PAGE_SIZE - 1))) {
return nullptr;
}
//create vmar large enough for rx,tx buffers, and rx,tx dma descriptors
vmar_mgr = fzl::VmarManager::Create(size, nullptr);
if (!vmar_mgr) {
zxlogf(ERROR, "pinned-buffer: Creation of vmar manager failed\n");
return nullptr;
}
fbl::AllocChecker ac;
auto pbuf = fbl::AdoptRef(new (&ac) PinnedBuffer());
if (!ac.check()) {
return nullptr;
}
zx_status_t status = pbuf->vmo_mapper_.CreateAndMap(size,
ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
std::move(vmar_mgr), &pbuf->vmo_,
ZX_RIGHT_READ | ZX_RIGHT_MAP |
ZX_RIGHT_WRITE,
cache_policy);
if (status != ZX_OK) {
zxlogf(ERROR, "pinned-buffer: vmo creation failed %d\n", status);
return nullptr;
}
uint32_t page_count = static_cast<uint32_t>(size / PAGE_SIZE);
fbl::unique_ptr<zx_paddr_t[]> addrs(new (&ac) zx_paddr_t[page_count]);
if (!ac.check()) {
return nullptr;
}
// Now actually pin the region.
status = bti.pin(ZX_BTI_PERM_READ | ZX_BTI_PERM_WRITE,
pbuf->vmo_, 0, size, addrs.get(),
page_count, &pbuf->pmt_);
if (status != ZX_OK) {
pbuf->UnPin();
return nullptr;
}
pbuf->paddrs_.reset(addrs.release());
return pbuf;
}
zx_status_t PinnedBuffer::UnPin() {
if ((paddrs_ == nullptr) || !pmt_.is_valid()) {
return ZX_ERR_BAD_STATE;
}
pmt_.unpin();
paddrs_.reset();
return ZX_OK;
}
// We need a status here since it is within the realm of possibility that
// the physical address returned could legitimately be 0x00000000, so
// returning a nullptr for a failure won't cut it.
zx_status_t PinnedBuffer::LookupPhys(zx_off_t offset, zx_paddr_t* out) {
if (paddrs_ == nullptr) {
return ZX_ERR_BAD_STATE;
}
if (offset >= GetSize()) {
*out = 0;
return ZX_ERR_INVALID_ARGS;
}
*out = paddrs_[offset / PAGE_SIZE] + (offset % PAGE_SIZE);
return ZX_OK;
}