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