blob: 72e953ee550c59a26116615a7f924da02f28b093 [file] [log] [blame]
// Copyright 2017 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 <dispatcher-pool/dispatcher-channel.h>
#include <dispatcher-pool/dispatcher-execution-domain.h>
#include <ddk/binding.h>
#include <ddk/driver.h>
#include <intel-hda/utils/intel-hda-registers.h>
#include <fbl/algorithm.h>
#include <fbl/alloc_checker.h>
#include <string.h>
#include <zircon/assert.h>
#include <zircon/device/intel-hda.h>
#include <zircon/process.h>
#include <lib/zx/channel.h>
#include <utility>
#include "debug-logging.h"
#include "utils.h"
namespace audio {
namespace intel_hda {
fbl::RefPtr<fzl::VmarManager> DriverVmars::registers_;
zx_status_t DriverVmars::Initialize() {
if (registers_ != nullptr) {
return ZX_ERR_BAD_STATE;
}
// Create a compact VMAR to map all of our registers into.
//
// TODO(johngro): See ZX-1822 for details.
//
// Sizing right now is a bit of a guessing game. A compact VMAR is not
// going to perfectly tightly pack everything; it will still insert random
// gaps in an attempt to get some minimum level of ASLR. For now, I'm using
// hardcoded guidance from teisenbe@ about how to size for the worst case.
// If/when there is a better way of doing this, I need to come back and
// switch to that.
//
// Formula being used here should be...
// 2 * (total_region_size + (512k * (total_allocations - 1)))
constexpr size_t MAX_SIZE_PER_CONTROLLER =
sizeof(hda_all_registers_t) +
MAPPED_CORB_RIRB_SIZE +
(MAX_STREAMS_PER_CONTROLLER * MAPPED_BDL_SIZE) +
sizeof(adsp_registers_t) +
MAPPED_BDL_SIZE;
// One alloc for the main registers, one for code loader BDL.
constexpr size_t MAX_ALLOCS_PER_DSP = 2;
// One alloc for the main registers, one for the CORB/RIRB, two for DSP,
// and one for each possible stream BDL.
constexpr size_t MAX_ALLOCS_PER_CONTROLLER = 2 + MAX_ALLOCS_PER_DSP +
MAX_STREAMS_PER_CONTROLLER;
constexpr size_t MAX_CONTROLLERS = 4;
constexpr size_t VMAR_SIZE = 2 *
((MAX_CONTROLLERS * MAX_SIZE_PER_CONTROLLER) +
(((MAX_CONTROLLERS * MAX_ALLOCS_PER_CONTROLLER) - 1) * (512u << 10)));
GLOBAL_LOG(TRACE, "Allocating 0x%zx byte VMAR for registers.\n", VMAR_SIZE);
registers_ = fzl::VmarManager::Create(VMAR_SIZE);
if (registers_ == nullptr) {
return ZX_ERR_NO_MEMORY;
}
return ZX_OK;
}
void DriverVmars::Shutdown() {
registers_.reset();
}
zx_status_t HandleDeviceIoctl(uint32_t op,
void* out_buf,
size_t out_len,
size_t* out_actual,
const fbl::RefPtr<dispatcher::ExecutionDomain>& domain,
dispatcher::Channel::ProcessHandler phandler,
dispatcher::Channel::ChannelClosedHandler chandler) {
if (op != IHDA_IOCTL_GET_CHANNEL) {
return ZX_ERR_NOT_SUPPORTED;
}
if ((out_buf == nullptr) ||
(out_actual == nullptr) ||
(out_len != sizeof(zx_handle_t))) {
return ZX_ERR_INVALID_ARGS;
}
zx::channel remote_endpoint_out;
zx_status_t res = CreateAndActivateChannel(domain,
std::move(phandler),
std::move(chandler),
nullptr,
&remote_endpoint_out);
if (res == ZX_OK) {
*(reinterpret_cast<zx_handle_t*>(out_buf)) = remote_endpoint_out.release();
*out_actual = sizeof(zx_handle_t);
}
return res;
}
zx_status_t CreateAndActivateChannel(const fbl::RefPtr<dispatcher::ExecutionDomain>& domain,
dispatcher::Channel::ProcessHandler phandler,
dispatcher::Channel::ChannelClosedHandler chandler,
fbl::RefPtr<dispatcher::Channel>* local_endpoint_out,
zx::channel* remote_endpoint_out) {
if (remote_endpoint_out == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
auto channel = dispatcher::Channel::Create();
if (channel == nullptr) {
return ZX_ERR_NO_MEMORY;
}
zx_status_t res = channel->Activate(remote_endpoint_out,
domain,
std::move(phandler),
std::move(chandler));
if ((res == ZX_OK) && (local_endpoint_out != nullptr)) {
*local_endpoint_out = std::move(channel);
}
return res;
}
} // namespace intel_hda
} // namespace audio