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 "shared-memory.h"
6
7#include <ddk/debug.h>
8#include <fbl/algorithm.h>
9#include <fbl/auto_call.h>
10#include <fbl/limits.h>
11
12namespace optee {
13
14SharedMemory::SharedMemory(zx_vaddr_t base_vaddr, zx_paddr_t base_paddr, RegionPtr region)
15    : base_vaddr_(base_vaddr), base_paddr_(base_paddr), region_(fbl::move(region)) {}
16
17zx_status_t SharedMemoryManager::Create(zx_paddr_t shared_mem_start,
18                                        size_t shared_mem_size,
19                                        fbl::unique_ptr<io_buffer_t> secure_world_memory,
20                                        fbl::unique_ptr<SharedMemoryManager>* out_manager) {
21    ZX_DEBUG_ASSERT(secure_world_memory != nullptr);
22    ZX_DEBUG_ASSERT(out_manager != nullptr);
23
24    auto io_buffer_cleanup = fbl::MakeAutoCall([io_buffer = secure_world_memory.get()]() {
25        io_buffer_release(io_buffer);
26    });
27
28    // Round the start and end to the nearest page boundaries within the range and calculate a
29    // new size.
30    shared_mem_start = fbl::round_up(shared_mem_start, static_cast<uint32_t>(PAGE_SIZE));
31    const zx_paddr_t shared_mem_end = fbl::round_down(shared_mem_start + shared_mem_size,
32                                                      static_cast<uint32_t>(PAGE_SIZE));
33    if (shared_mem_end <= shared_mem_start) {
34        zxlogf(ERROR, "optee: no shared memory available from secure world\n");
35        return ZX_ERR_NO_RESOURCES;
36    }
37    shared_mem_size = shared_mem_end - shared_mem_start;
38
39    // The secure world shared memory exists within some subrange of the secure_world_memory.
40    // Get the addresses from the io_buffer and validate that the requested subrange is within
41    // the mmio range.
42    const zx_vaddr_t secure_world_vaddr = reinterpret_cast<zx_vaddr_t>(io_buffer_virt(
43        secure_world_memory.get()));
44    const zx_paddr_t secure_world_paddr = io_buffer_phys(secure_world_memory.get());
45    const size_t secure_world_size = io_buffer_size(secure_world_memory.get(), 0);
46
47    if ((shared_mem_start < secure_world_paddr) ||
48        (shared_mem_end > secure_world_paddr + secure_world_size)) {
49        zxlogf(ERROR, "optee: shared memory not within secure os memory\n");
50        return ZX_ERR_INTERNAL;
51    }
52
53    if (shared_mem_size < 2 * kDriverPoolSize) {
54        zxlogf(ERROR, "optee: shared memory is not large enough\n");
55        return ZX_ERR_NO_RESOURCES;
56    }
57
58    const zx_off_t shared_mem_offset = shared_mem_start - secure_world_paddr;
59
60    fbl::AllocChecker ac;
61    fbl::unique_ptr<SharedMemoryManager> manager(new (&ac) SharedMemoryManager(
62        secure_world_vaddr + shared_mem_offset,
63        secure_world_paddr + shared_mem_offset,
64        shared_mem_size,
65        fbl::move(secure_world_memory)));
66
67    if (!ac.check()) {
68        return ZX_ERR_NO_MEMORY;
69    }
70
71    // We've successfully created the Manager and it now owns the io_buffer memory
72    io_buffer_cleanup.cancel();
73
74    *out_manager = fbl::move(manager);
75    return ZX_OK;
76}
77
78SharedMemoryManager::~SharedMemoryManager() {
79    io_buffer_release(secure_world_memory_.get());
80}
81
82SharedMemoryManager::SharedMemoryManager(zx_vaddr_t base_vaddr,
83                                         zx_paddr_t base_paddr,
84                                         size_t total_size,
85                                         fbl::unique_ptr<io_buffer_t> secure_world_memory)
86    : secure_world_memory_(fbl::move(secure_world_memory)),
87      driver_pool_(base_vaddr, base_paddr, kDriverPoolSize),
88      client_pool_(base_vaddr + kDriverPoolSize,
89                   base_paddr + kDriverPoolSize,
90                   total_size - kDriverPoolSize) {}
91
92} // namespace optee
93