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 <ddk/mmio-buffer.h>
6 
7 #include <string.h>
8 
9 #include <ddk/driver.h>
10 #include <zircon/process.h>
11 #include <zircon/syscalls.h>
12 #include <zircon/types.h>
13 
mmio_buffer_init(mmio_buffer_t * buffer,zx_off_t offset,size_t size,zx_handle_t vmo,uint32_t cache_policy)14 zx_status_t mmio_buffer_init(mmio_buffer_t* buffer, zx_off_t offset, size_t size,
15                              zx_handle_t vmo, uint32_t cache_policy) {
16     zx_status_t status = zx_vmo_set_cache_policy(vmo, cache_policy);
17     if (status != ZX_OK) {
18         zx_handle_close(vmo);
19         return status;
20     }
21 
22     uintptr_t vaddr;
23     const size_t vmo_offset = ROUNDDOWN(offset, ZX_PAGE_SIZE);
24     const size_t page_offset = offset - vmo_offset;
25     const size_t vmo_size = ROUNDUP(size + page_offset, ZX_PAGE_SIZE);
26 
27     status = zx_vmar_map(zx_vmar_root_self(),
28                          ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_MAP_RANGE,
29                          0, vmo, vmo_offset, vmo_size, &vaddr);
30     if (status != ZX_OK) {
31         zx_handle_close(vmo);
32         return status;
33     }
34 
35     buffer->vmo = vmo;
36     buffer->vaddr = (void*)(vaddr + page_offset);
37     buffer->offset = offset;
38     buffer->size = size;
39 
40     return ZX_OK;
41 }
42 
mmio_buffer_init_physical(mmio_buffer_t * buffer,zx_paddr_t base,size_t size,zx_handle_t resource,uint32_t cache_policy)43 zx_status_t mmio_buffer_init_physical(mmio_buffer_t* buffer, zx_paddr_t base, size_t size,
44                                       zx_handle_t resource, uint32_t cache_policy) {
45     zx_handle_t vmo;
46     zx_status_t status = zx_vmo_create_physical(resource, base, size, &vmo);
47     if (status != ZX_OK) {
48         return status;
49     }
50 
51     // |base| is guaranteed to be page aligned.
52     return mmio_buffer_init(buffer, 0, size, vmo, cache_policy);
53 }
54 
mmio_buffer_release(mmio_buffer_t * buffer)55 void mmio_buffer_release(mmio_buffer_t* buffer) {
56     if (buffer->vmo != ZX_HANDLE_INVALID) {
57         zx_vmar_unmap(zx_vmar_root_self(), (uintptr_t)buffer->vaddr, buffer->size);
58         zx_handle_close(buffer->vmo);
59         buffer->vmo = ZX_HANDLE_INVALID;
60     }
61 }
62 
mmio_buffer_pin(mmio_buffer_t * buffer,zx_handle_t bti,mmio_pinned_buffer_t * out)63 zx_status_t mmio_buffer_pin(mmio_buffer_t* buffer, zx_handle_t bti, mmio_pinned_buffer_t* out) {
64     zx_paddr_t paddr;
65     zx_handle_t pmt;
66     const uint32_t options = ZX_BTI_PERM_WRITE | ZX_BTI_PERM_READ | ZX_BTI_CONTIGUOUS;
67     const size_t vmo_offset = ROUNDDOWN(buffer->offset, ZX_PAGE_SIZE);
68     const size_t page_offset = buffer->offset - vmo_offset;
69     const size_t vmo_size = ROUNDUP(buffer->size + page_offset, ZX_PAGE_SIZE);
70 
71     zx_status_t status = zx_bti_pin(bti, options, buffer->vmo, vmo_offset, vmo_size,
72                                     &paddr, 1, &pmt);
73     if (status != ZX_OK) {
74         return status;
75     }
76 
77     out->mmio = buffer;
78     out->paddr = paddr + page_offset;
79     out->pmt = pmt;
80 
81     return ZX_OK;
82 }
83 
mmio_buffer_unpin(mmio_pinned_buffer_t * buffer)84 void mmio_buffer_unpin(mmio_pinned_buffer_t* buffer) {
85     if (buffer->pmt != ZX_HANDLE_INVALID) {
86         zx_pmt_unpin(buffer->pmt);
87         buffer->pmt = ZX_HANDLE_INVALID;
88     }
89 }
90