[virtio][entropy] Basic virtio-rng driver

The driver draws entropy from the host system on qemu. Since there
isn't a mechanism for the kernel to pull entropy from the driver or to
control the driver's rate of activity, for now the driver just launches
its own thread to push entropy to the kernel periodically.

Change-Id: I950518db4a5776f041856b05ad0277b970aa6bc5
Esse commit está contido em:
Andrew Krieger
2017-09-11 14:41:59 -07:00
commit de CQ bot account: commit-bot@chromium.org
commit 6534d9a705
5 arquivos alterados com 203 adições e 1 exclusões
+140
Ver Arquivo
@@ -0,0 +1,140 @@
// 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 "rng.h"
#include <ddk/debug.h>
#include <inttypes.h>
#include <fbl/auto_lock.h>
namespace virtio {
RngDevice::RngDevice(mx_device_t* bus_device)
: Device(bus_device) {
}
RngDevice::~RngDevice() {
// TODO: clean up allocated physical memory
}
mx_status_t RngDevice::Init() {
// reset the device
Reset();
// ack and set the driver status bit
StatusAcknowledgeDriver();
// allocate the main vring
auto err = vring_.Init(kRingIndex, kRingSize);
if (err < 0) {
dprintf(ERROR, "virtio-rng: failed to allocate vring\n");
return err;
}
// allocate the entropy buffer
mx_status_t rc = io_buffer_init(&buf_, kBufferSize, IO_BUFFER_RO);
if (rc != MX_OK) {
dprintf(ERROR, "virtio-rng: cannot allocate entropy buffer: %d\n", rc);
return rc;
}
dprintf(SPEW, "virtio-rng: allocated entropy buffer at %p, physical address %#" PRIxPTR "\n",
io_buffer_virt(&buf_), io_buffer_phys(&buf_));
// start the interrupt thread
StartIrqThread();
// set DRIVER_OK
StatusDriverOK();
device_add_args_t args = {};
args.version = DEVICE_ADD_ARGS_VERSION;
args.name = "virtio-rng";
args.ctx = nullptr;
args.ops = &device_ops_;
auto status = device_add(bus_device_, &args, &device_);
if (status < 0) {
dprintf(ERROR, "virtio-rng: device_add failed %d\n", status);
device_ = nullptr;
return status;
}
// TODO(SEC-29): The kernel should trigger entropy requests, instead of relying on this
// userspace thread to push entropy whenever it wants to. As a temporary hack, this thread
// pushes entropy to the kernel every 300 seconds instead.
thrd_create_with_name(&seed_thread_, RngDevice::SeedThreadEntry, this,
"virtio-rng-seed-thread");
thrd_detach(seed_thread_);
dprintf(INFO, "virtio-rng: initialization succeeded\n");
return MX_OK;
}
void RngDevice::IrqRingUpdate() {
dprintf(TRACE, "virtio-rng: Got irq ring update\n");
// parse our descriptor chain, add back to the free queue
auto free_chain = [this](vring_used_elem* used_elem) {
uint32_t i = (uint16_t)used_elem->id;
struct vring_desc* desc = vring_.DescFromIndex((uint16_t)i);
if (desc->addr != io_buffer_phys(&buf_) || desc->len != kBufferSize) {
dprintf(ERROR, "virtio-rng: entropy response with unexpected buffer\n");
} else {
dprintf(SPEW, "virtio-rng: received entropy; adding to kernel pool\n");
mx_status_t rc = mx_cprng_add_entropy(io_buffer_virt(&buf_), kBufferSize);
if (rc != MX_OK) {
dprintf(ERROR, "virtio-rng: add_entropy failed (%d)\n", rc);
}
}
vring_.FreeDesc((uint16_t)i);
};
// tell the ring to find free chains and hand it back to our lambda
vring_.IrqRingUpdate(free_chain);
}
void RngDevice::IrqConfigChange() {
dprintf(TRACE, "virtio-rng: Got irq config change (ignoring)\n");
}
int RngDevice::SeedThreadEntry(void* arg) {
RngDevice* d = static_cast<RngDevice*>(arg);
for(;;) {
mx_status_t rc = d->Request();
dprintf(SPEW, "virtio-rng-seed-thread: RngDevice::Request() returned %d\n", rc);
mx_nanosleep(mx_deadline_after(MX_SEC(300)));
}
}
mx_status_t RngDevice::Request() {
dprintf(TRACE, "virtio-rng: sending entropy request\n");
fbl::AutoLock lock(&lock_);
uint16_t i;
vring_desc* desc = vring_.AllocDescChain(1, &i);
if (!desc) {
dprintf(ERROR, "virtio-rng: failed to allocate descriptor chain of length 1\n");
return MX_ERR_NO_RESOURCES;
}
desc->addr = io_buffer_phys(&buf_);
desc->len = kBufferSize;
desc->flags = VRING_DESC_F_WRITE;
dprintf(SPEW, "virtio-rng: allocated descriptor chain desc %p, i %u\n", desc, i);
if (driver_get_log_flags() & DDK_LOG_SPEW) {
virtio_dump_desc(desc);
}
vring_.SubmitChain(i);
vring_.Kick();
dprintf(SPEW, "virtio-rng: kicked off entropy request\n");
return MX_OK;
}
} // namespace virtio
+53
Ver Arquivo
@@ -0,0 +1,53 @@
// 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.
#pragma once
#include "device.h"
#include "ring.h"
#include <ddk/io-buffer.h>
#include <magenta/compiler.h>
#include <stdlib.h>
namespace virtio {
class Ring;
class RngDevice : public Device {
public:
RngDevice(mx_device_t* device);
virtual ~RngDevice();
virtual mx_status_t Init();
virtual void IrqRingUpdate();
virtual void IrqConfigChange();
private:
// TODO(SEC-29): The kernel should trigger entropy requests, instead of relying on this
// userspace thread to push entropy whenever it wants to. As a temporary hack, this thread
// pushes entropy to the kernel every 300 seconds instead.
// the entry point for the entropy seeding thread
static int SeedThreadEntry(void* arg);
// the method called by SeedThreadEntry() to actually launch a request
mx_status_t Request();
// the thread that seeds the system CPRNG periodically
thrd_t seed_thread_;
// the virtio ring
static constexpr uint16_t kRingIndex = 0;
static constexpr uint16_t kRingSize = 1;
Ring vring_ = {this};
// the buffer used to receive entropy
static constexpr size_t kBufferSize = MX_CPRNG_ADD_ENTROPY_MAX_LEN;
io_buffer_t buf_;
};
} // namespace virtio
+1
Ver Arquivo
@@ -14,6 +14,7 @@ MODULE_SRCS := \
$(LOCAL_DIR)/ethernet.cpp \
$(LOCAL_DIR)/gpu.cpp \
$(LOCAL_DIR)/ring.cpp \
$(LOCAL_DIR)/rng.cpp \
$(LOCAL_DIR)/utils.cpp \
$(LOCAL_DIR)/virtio_c.c \
$(LOCAL_DIR)/virtio_driver.cpp \
+3 -1
Ver Arquivo
@@ -20,12 +20,14 @@ static mx_driver_ops_t virtio_driver_ops = {
.bind = virtio_bind,
};
MAGENTA_DRIVER_BEGIN(virtio, virtio_driver_ops, "magenta", "0.1", 7)
MAGENTA_DRIVER_BEGIN(virtio, virtio_driver_ops, "magenta", "0.1", 9)
BI_ABORT_IF(NE, BIND_PROTOCOL, MX_PROTOCOL_PCI),
BI_ABORT_IF(NE, BIND_PCI_VID, 0x1af4),
BI_MATCH_IF(EQ, BIND_PCI_DID, 0x1000), // Network device (transitional)
BI_MATCH_IF(EQ, BIND_PCI_DID, 0x1001), // Block device (transitional)
BI_MATCH_IF(EQ, BIND_PCI_DID, 0x1042), // Block device
BI_MATCH_IF(EQ, BIND_PCI_DID, 0x1005), // RNG device (transitional)
BI_MATCH_IF(EQ, BIND_PCI_DID, 0x1044), // RNG device
BI_MATCH_IF(EQ, BIND_PCI_DID, 0x1050), // GPU device
BI_ABORT(),
MAGENTA_DRIVER_END(virtio)
+6
Ver Arquivo
@@ -19,6 +19,7 @@
#include "block.h"
#include "device.h"
#include "rng.h"
#include "ethernet.h"
#include "gpu.h"
#include "trace.h"
@@ -67,6 +68,11 @@ extern "C" mx_status_t virtio_bind(void* ctx, mx_device_t* device, void** cookie
LTRACEF("found gpu device\n");
vd.reset(new virtio::GpuDevice(device));
break;
case 0x1005:
case 0x1044:
LTRACEF("found entropy device\n");
vd.reset(new virtio::RngDevice(device));
break;
default:
printf("unhandled device id, how did this happen?\n");
return -1;