blob: ea97e69c1522a3e2e4728945cb254e2f15e359d4 [file] [log] [blame]
// Copyright 2019 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 <memory>
#include <ddk/driver.h>
#include <ddk/binding.h>
#include <ddktl/device.h>
#include <fuchsia/hardware/ramdisk/c/fidl.h>
#include <lib/fidl-utils/bind.h>
#include <lib/zx/vmo.h>
#include <zircon/types.h>
#include "ramdisk.h"
#include "transaction.h"
namespace ramdisk {
namespace {
class RamdiskController;
using RamdiskControllerDeviceType = ddk::Device<RamdiskController, ddk::Messageable>;
class RamdiskController : public RamdiskControllerDeviceType {
public:
RamdiskController(zx_device_t* parent) : RamdiskControllerDeviceType(parent) {}
// Device Protocol
zx_status_t DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn);
void DdkRelease() {
delete this;
}
private:
// FIDL Interface RamdiskController.
zx_status_t Create(uint64_t block_size, uint64_t block_count,
const fuchsia_hardware_ramdisk_GUID* type_guid, fidl_txn_t* txn);
zx_status_t CreateFromVmo(zx_handle_t vmo, fidl_txn_t* txn);
// Other methods
zx_status_t ConfigureDevice(zx::vmo vmo, uint64_t block_size, uint64_t block_count,
const uint8_t* type_guid, char* name, size_t buffer_size,
size_t* name_length);
static const fuchsia_hardware_ramdisk_RamdiskController_ops* Ops() {
using Binder = fidl::Binder<RamdiskController>;
static const fuchsia_hardware_ramdisk_RamdiskController_ops kOps = {
.Create = Binder::BindMember<&RamdiskController::Create>,
.CreateFromVmo = Binder::BindMember<&RamdiskController::CreateFromVmo>,
};
return &kOps;
}
};
zx_status_t RamdiskController::DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn) {
return fuchsia_hardware_ramdisk_RamdiskController_dispatch(this, txn, msg, Ops());
}
zx_status_t RamdiskController::Create(uint64_t block_size, uint64_t block_count,
const fuchsia_hardware_ramdisk_GUID* type_guid, fidl_txn_t* txn) {
auto failure_response = [&txn](zx_status_t status) -> zx_status_t {
return fuchsia_hardware_ramdisk_RamdiskControllerCreate_reply(txn, status, nullptr, 0);
};
zx::vmo vmo;
zx_status_t status = zx::vmo::create(block_size * block_count, 0, &vmo);
if (status != ZX_OK) {
return failure_response(status);
}
char name[fuchsia_hardware_ramdisk_MAX_NAME_LENGTH + 1];
size_t name_length = 0;
status = ConfigureDevice(std::move(vmo), block_size, block_count,
type_guid ? type_guid->value : nullptr,
name, sizeof(name), &name_length);
if (status != ZX_OK) {
return failure_response(status);
}
return fuchsia_hardware_ramdisk_RamdiskControllerCreate_reply(txn, ZX_OK, name, name_length);
}
zx_status_t RamdiskController::CreateFromVmo(zx_handle_t raw_vmo, fidl_txn_t* txn) {
zx::vmo vmo(raw_vmo);
auto failure_response = [&txn](zx_status_t status) -> zx_status_t {
return fuchsia_hardware_ramdisk_RamdiskControllerCreateFromVmo_reply(txn, status,
nullptr, 0);
};
// Ensure this is the last handle to this VMO; otherwise, the size
// may change from underneath us.
zx_info_handle_count_t info;
zx_status_t status = vmo.get_info(ZX_INFO_HANDLE_COUNT, &info, sizeof(info), nullptr,
nullptr);
if (status != ZX_OK || info.handle_count != 1) {
return failure_response(ZX_ERR_INVALID_ARGS);
}
uint64_t vmo_size;
status = vmo.get_size(&vmo_size);
if (status != ZX_OK) {
return failure_response(status);
}
char name[fuchsia_hardware_ramdisk_MAX_NAME_LENGTH + 1];
size_t name_length = 0;
status = ConfigureDevice(std::move(vmo), PAGE_SIZE, (vmo_size + PAGE_SIZE - 1) / PAGE_SIZE,
nullptr, name, sizeof(name), &name_length);
if (status != ZX_OK) {
return failure_response(status);
}
return fuchsia_hardware_ramdisk_RamdiskControllerCreateFromVmo_reply(txn, ZX_OK, name,
name_length);
}
constexpr size_t kMaxRamdiskNameLength = 32;
zx_status_t RamdiskController::ConfigureDevice(zx::vmo vmo, uint64_t block_size,
uint64_t block_count, const uint8_t* type_guid,
char* name, size_t buffer_size,
size_t* name_length) {
if (buffer_size < kMaxRamdiskNameLength) {
return ZX_ERR_INVALID_ARGS;
}
std::unique_ptr<Ramdisk> ramdev;
zx_status_t status = Ramdisk::Create(zxdev(), std::move(vmo), block_size, block_count,
type_guid, &ramdev);
if (status != ZX_OK) {
return status;
}
size_t namelen = strlcpy(name, ramdev->Name(), buffer_size);
if ((status = ramdev->DdkAdd(ramdev->Name()) != ZX_OK)) {
ramdev.release()->DdkRelease();
return status;
}
__UNUSED auto ptr = ramdev.release();
*name_length = namelen;
return ZX_OK;
}
zx_status_t RamdiskDriverBind(void* ctx, zx_device_t* parent) {
auto ramctl = std::make_unique<RamdiskController>(parent);
zx_status_t status = ramctl->DdkAdd("ramctl");
if (status != ZX_OK) {
return status;
}
// RamdiskController owned by the DDK after being added successfully.
__UNUSED auto ptr = ramctl.release();
return ZX_OK;
}
zx_driver_ops_t ramdisk_driver_ops = []() {
zx_driver_ops_t ops = {};
ops.version = DRIVER_OPS_VERSION;
ops.bind = RamdiskDriverBind;
return ops;
}();
} // namespace
} // namespace ramdisk
ZIRCON_DRIVER_BEGIN(ramdisk, ramdisk::ramdisk_driver_ops, "zircon", "0.1", 1)
BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_MISC_PARENT),
ZIRCON_DRIVER_END(ramdisk)