1// Copyright 2017 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 <dispatcher-pool/dispatcher-channel.h>
6#include <dispatcher-pool/dispatcher-execution-domain.h>
7#include <ddk/binding.h>
8#include <ddk/driver.h>
9#include <intel-hda/utils/intel-hda-registers.h>
10#include <fbl/algorithm.h>
11#include <fbl/alloc_checker.h>
12#include <string.h>
13#include <zircon/assert.h>
14#include <zircon/device/intel-hda.h>
15#include <zircon/process.h>
16#include <lib/zx/channel.h>
17
18#include "debug-logging.h"
19#include "utils.h"
20
21namespace audio {
22namespace intel_hda {
23
24fbl::RefPtr<fzl::VmarManager> DriverVmars::registers_;
25
26zx_status_t DriverVmars::Initialize() {
27    if (registers_ != nullptr) {
28        return ZX_ERR_BAD_STATE;
29    }
30
31    // Create a compact VMAR to map all of our registers into.
32    //
33    // TODO(johngro): See ZX-1822 for details.
34    //
35    // Sizing right now is a bit of a guessing game.  A compact VMAR is not
36    // going to perfectly tightly pack everything; it will still insert random
37    // gaps in an attempt to get some minimum level of ASLR.  For now, I'm using
38    // hardcoded guidance from teisenbe@ about how to size for the worst case.
39    // If/when there is a better way of doing this, I need to come back and
40    // switch to that.
41    //
42    // Formula being used here should be...
43    // 2 * (total_region_size + (512k * (total_allocations - 1)))
44    constexpr size_t MAX_SIZE_PER_CONTROLLER =
45        sizeof(hda_all_registers_t) +
46        MAPPED_CORB_RIRB_SIZE +
47        (MAX_STREAMS_PER_CONTROLLER * MAPPED_BDL_SIZE) +
48        sizeof(adsp_registers_t) +
49        MAPPED_BDL_SIZE;
50
51    // One alloc for the main registers, one for code loader BDL.
52    constexpr size_t MAX_ALLOCS_PER_DSP = 2;
53    // One alloc for the main registers, one for the CORB/RIRB, two for DSP,
54    // and one for each possible stream BDL.
55    constexpr size_t MAX_ALLOCS_PER_CONTROLLER = 2 + MAX_ALLOCS_PER_DSP +
56                                                 MAX_STREAMS_PER_CONTROLLER;
57    constexpr size_t MAX_CONTROLLERS = 4;
58    constexpr size_t VMAR_SIZE = 2 *
59        ((MAX_CONTROLLERS * MAX_SIZE_PER_CONTROLLER) +
60        (((MAX_CONTROLLERS * MAX_ALLOCS_PER_CONTROLLER) - 1) * (512u << 10)));
61
62    GLOBAL_LOG(TRACE, "Allocating 0x%zx byte VMAR for registers.\n", VMAR_SIZE);
63    registers_ = fzl::VmarManager::Create(VMAR_SIZE);
64    if (registers_ == nullptr) {
65        return ZX_ERR_NO_MEMORY;
66    }
67
68    return ZX_OK;
69}
70
71void DriverVmars::Shutdown() {
72    registers_.reset();
73}
74
75zx_status_t HandleDeviceIoctl(uint32_t op,
76                              void* out_buf,
77                              size_t out_len,
78                              size_t* out_actual,
79                              const fbl::RefPtr<dispatcher::ExecutionDomain>& domain,
80                              dispatcher::Channel::ProcessHandler phandler,
81                              dispatcher::Channel::ChannelClosedHandler chandler) {
82    if (op != IHDA_IOCTL_GET_CHANNEL) {
83        return ZX_ERR_NOT_SUPPORTED;
84    }
85
86    if ((out_buf == nullptr) ||
87        (out_actual == nullptr) ||
88        (out_len != sizeof(zx_handle_t))) {
89        return ZX_ERR_INVALID_ARGS;
90    }
91
92    zx::channel remote_endpoint_out;
93    zx_status_t res = CreateAndActivateChannel(domain,
94                                               fbl::move(phandler),
95                                               fbl::move(chandler),
96                                               nullptr,
97                                               &remote_endpoint_out);
98    if (res == ZX_OK) {
99        *(reinterpret_cast<zx_handle_t*>(out_buf)) = remote_endpoint_out.release();
100        *out_actual = sizeof(zx_handle_t);
101    }
102
103    return res;
104}
105
106zx_status_t CreateAndActivateChannel(const fbl::RefPtr<dispatcher::ExecutionDomain>& domain,
107                                     dispatcher::Channel::ProcessHandler phandler,
108                                     dispatcher::Channel::ChannelClosedHandler chandler,
109                                     fbl::RefPtr<dispatcher::Channel>* local_endpoint_out,
110                                     zx::channel* remote_endpoint_out) {
111    if (remote_endpoint_out == nullptr) {
112        return ZX_ERR_INVALID_ARGS;
113    }
114
115    auto channel = dispatcher::Channel::Create();
116    if (channel == nullptr) {
117        return ZX_ERR_NO_MEMORY;
118    }
119
120    zx_status_t res = channel->Activate(remote_endpoint_out,
121                                        domain,
122                                        fbl::move(phandler),
123                                        fbl::move(chandler));
124    if ((res == ZX_OK) && (local_endpoint_out != nullptr)) {
125        *local_endpoint_out = fbl::move(channel);
126    }
127
128    return res;
129}
130
131}  // namespace intel_hda
132}  // namespace audio
133