| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "shared-memory.h" |
| |
| #include <ddk/debug.h> |
| #include <fbl/algorithm.h> |
| #include <fbl/auto_call.h> |
| |
| namespace optee { |
| |
| std::optional<SharedMemoryView> SharedMemoryRangeTraits::SliceByVaddr(zx_vaddr_t start, |
| zx_vaddr_t end) const { |
| if (end <= start || !ContainsVaddr(start) || !ContainsVaddr(end - 1)) { |
| return std::nullopt; |
| } |
| |
| zx_off_t offset = start - vaddr(); |
| return std::make_optional(SharedMemoryView(start, paddr() + offset, end - start)); |
| } |
| |
| std::optional<SharedMemoryView> SharedMemoryRangeTraits::SliceByPaddr(zx_paddr_t start, |
| zx_paddr_t end) const { |
| if (end <= start || !ContainsPaddr(start) || !ContainsPaddr(end - 1)) { |
| return std::nullopt; |
| } |
| |
| zx_off_t offset = start - paddr(); |
| return std::make_optional(SharedMemoryView(vaddr() + offset, start, end - start)); |
| } |
| |
| zx_status_t SharedMemoryManager::Create(zx_paddr_t shared_mem_start, |
| size_t shared_mem_size, |
| ddk::MmioBuffer secure_world_memory, |
| zx::bti bti, |
| fbl::unique_ptr<SharedMemoryManager>* out_manager) { |
| ZX_DEBUG_ASSERT(out_manager != nullptr); |
| |
| // Round the start and end to the nearest page boundaries within the range and calculate a |
| // new size. |
| shared_mem_start = fbl::round_up(shared_mem_start, static_cast<uint32_t>(PAGE_SIZE)); |
| const zx_paddr_t shared_mem_end = fbl::round_down(shared_mem_start + shared_mem_size, |
| static_cast<uint32_t>(PAGE_SIZE)); |
| if (shared_mem_end <= shared_mem_start) { |
| zxlogf(ERROR, "optee: no shared memory available from secure world\n"); |
| return ZX_ERR_NO_RESOURCES; |
| } |
| shared_mem_size = shared_mem_end - shared_mem_start; |
| |
| std::optional<ddk::MmioPinnedBuffer> pinned; |
| zx_status_t status = secure_world_memory.Pin(bti, &pinned); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "optee: unable to pin secure world memory\n"); |
| return status; |
| } |
| |
| // The secure world shared memory exists within some subrange of the secure_world_memory. |
| // Get the addresses from the io_buffer and validate that the requested subrange is within |
| // the mmio range. |
| const zx_vaddr_t secure_world_vaddr = reinterpret_cast<zx_vaddr_t>(secure_world_memory.get()); |
| const zx_paddr_t secure_world_paddr = pinned->get_paddr(); |
| const size_t secure_world_size = secure_world_memory.get_size(); |
| |
| if ((shared_mem_start < secure_world_paddr) || |
| (shared_mem_end > secure_world_paddr + secure_world_size)) { |
| zxlogf(ERROR, "optee: shared memory not within secure os memory\n"); |
| return ZX_ERR_INTERNAL; |
| } |
| |
| if (shared_mem_size < 2 * kDriverPoolSize) { |
| zxlogf(ERROR, "optee: shared memory is not large enough\n"); |
| return ZX_ERR_NO_RESOURCES; |
| } |
| |
| const zx_off_t shared_mem_offset = shared_mem_start - secure_world_paddr; |
| |
| fbl::AllocChecker ac; |
| fbl::unique_ptr<SharedMemoryManager> manager(new (&ac) SharedMemoryManager( |
| secure_world_vaddr + shared_mem_offset, |
| secure_world_paddr + shared_mem_offset, |
| shared_mem_size, |
| std::move(secure_world_memory), |
| *std::move(pinned))); |
| |
| if (!ac.check()) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| *out_manager = std::move(manager); |
| return ZX_OK; |
| } |
| |
| SharedMemoryManager::SharedMemoryManager(zx_vaddr_t base_vaddr, |
| zx_paddr_t base_paddr, |
| size_t total_size, |
| ddk::MmioBuffer secure_world_memory, |
| ddk::MmioPinnedBuffer secure_world_memory_pin) |
| : secure_world_memory_(std::move(secure_world_memory)), |
| secure_world_memory_pin_(std::move(secure_world_memory_pin)), |
| driver_pool_(base_vaddr, base_paddr, kDriverPoolSize), |
| client_pool_(base_vaddr + kDriverPoolSize, |
| base_paddr + kDriverPoolSize, |
| total_size - kDriverPoolSize) {} |
| |
| } // namespace optee |