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 "pinned-buffer.h"
6
7 #include <climits>
8 #include <utility>
9
Create(size_t size,const zx::bti & bti,uint32_t cache_policy)10 fbl::RefPtr<PinnedBuffer> PinnedBuffer::Create(size_t size, const zx::bti& bti,
11 uint32_t cache_policy) {
12 fbl::RefPtr<fzl::VmarManager> vmar_mgr;
13
14 if (!bti.is_valid() || (size & (PAGE_SIZE - 1))) {
15 return nullptr;
16 }
17
18 //create vmar large enough for rx,tx buffers, and rx,tx dma descriptors
19 vmar_mgr = fzl::VmarManager::Create(size, nullptr);
20 if (!vmar_mgr) {
21 zxlogf(ERROR, "pinned-buffer: Creation of vmar manager failed\n");
22 return nullptr;
23 }
24
25 fbl::AllocChecker ac;
26
27 auto pbuf = fbl::AdoptRef(new (&ac) PinnedBuffer());
28 if (!ac.check()) {
29 return nullptr;
30 }
31
32 zx_status_t status = pbuf->vmo_mapper_.CreateAndMap(size,
33 ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
34 std::move(vmar_mgr), &pbuf->vmo_,
35 ZX_RIGHT_READ | ZX_RIGHT_MAP |
36 ZX_RIGHT_WRITE,
37 cache_policy);
38 if (status != ZX_OK) {
39 zxlogf(ERROR, "pinned-buffer: vmo creation failed %d\n", status);
40 return nullptr;
41 }
42
43 uint32_t page_count = static_cast<uint32_t>(size / PAGE_SIZE);
44
45 fbl::unique_ptr<zx_paddr_t[]> addrs(new (&ac) zx_paddr_t[page_count]);
46 if (!ac.check()) {
47 return nullptr;
48 }
49
50 // Now actually pin the region.
51
52 status = bti.pin(ZX_BTI_PERM_READ | ZX_BTI_PERM_WRITE,
53 pbuf->vmo_, 0, size, addrs.get(),
54 page_count, &pbuf->pmt_);
55 if (status != ZX_OK) {
56 pbuf->UnPin();
57 return nullptr;
58 }
59
60 pbuf->paddrs_.reset(addrs.release());
61 return pbuf;
62 }
63
UnPin()64 zx_status_t PinnedBuffer::UnPin() {
65 if ((paddrs_ == nullptr) || !pmt_.is_valid()) {
66 return ZX_ERR_BAD_STATE;
67 }
68 pmt_.unpin();
69 paddrs_.reset();
70 return ZX_OK;
71 }
72
73 // We need a status here since it is within the realm of possibility that
74 // the physical address returned could legitimately be 0x00000000, so
75 // returning a nullptr for a failure won't cut it.
LookupPhys(zx_off_t offset,zx_paddr_t * out)76 zx_status_t PinnedBuffer::LookupPhys(zx_off_t offset, zx_paddr_t* out) {
77 if (paddrs_ == nullptr) {
78 return ZX_ERR_BAD_STATE;
79 }
80 if (offset >= GetSize()) {
81 *out = 0;
82 return ZX_ERR_INVALID_ARGS;
83 }
84
85 *out = paddrs_[offset / PAGE_SIZE] + (offset % PAGE_SIZE);
86
87 return ZX_OK;
88 }
89