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
14zx_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
43void mmio_buffer_release(mmio_buffer_t* buffer) {
44    if (buffer->vmo != ZX_HANDLE_INVALID) {
45        zx_vmar_unmap(zx_vmar_root_self(), (uintptr_t)buffer->vaddr, buffer->size);
46        zx_handle_close(buffer->vmo);
47        buffer->vmo = ZX_HANDLE_INVALID;
48    }
49}
50
51zx_status_t mmio_buffer_pin(mmio_buffer_t* buffer, zx_handle_t bti, mmio_pinned_buffer_t* out) {
52    zx_paddr_t paddr;
53    zx_handle_t pmt;
54    const uint32_t options = ZX_BTI_PERM_WRITE | ZX_BTI_PERM_READ;
55    const size_t vmo_offset = ROUNDDOWN(buffer->offset, ZX_PAGE_SIZE);
56    const size_t page_offset = buffer->offset - vmo_offset;
57    const size_t vmo_size = ROUNDUP(buffer->size + page_offset, ZX_PAGE_SIZE);
58
59    zx_status_t status = zx_bti_pin(bti, options, buffer->vmo, vmo_offset, vmo_size,
60                                    &paddr, 1, &pmt);
61    if (status != ZX_OK) {
62        return status;
63    }
64
65    out->mmio = buffer;
66    out->paddr = paddr + page_offset;
67    out->pmt = pmt;
68
69    return ZX_OK;
70}
71
72void mmio_buffer_unpin(mmio_pinned_buffer_t* buffer) {
73    if (buffer->pmt != ZX_HANDLE_INVALID) {
74        zx_pmt_unpin(buffer->pmt);
75        buffer->pmt = ZX_HANDLE_INVALID;
76    }
77}
78