ccp: driver for the RNG part of CCP on AMD

the entropy source is read every second and pushed to the PRNG.
the PCI device is tested, not the ACPI.

Change-Id: I9bb6b21c7189b28a1d8a624d83b33ff6682152dc
Reviewed-on: https://review.haiku-os.org/c/haiku/+/5825
Tested-by: Commit checker robot <[email protected]>
Reviewed-by: Jérôme Duval <[email protected]>
This commit is contained in:
Jérôme Duval
2022-11-28 13:33:01 +00:00
parent bce7dc883a
commit 652d5f6347
9 changed files with 601 additions and 2 deletions
+1 -1
View File
@@ -30,7 +30,7 @@ AddFilesToPackage add-ons kernel busses ata
AddFilesToPackage add-ons kernel busses i2c : pch_i2c@x86,x86_64 ; AddFilesToPackage add-ons kernel busses i2c : pch_i2c@x86,x86_64 ;
AddFilesToPackage add-ons kernel busses mmc : sdhci_pci ; AddFilesToPackage add-ons kernel busses mmc : sdhci_pci ;
AddFilesToPackage add-ons kernel busses random : virtio_rng ; AddFilesToPackage add-ons kernel busses random : ccp_rng@x86,x86_64 virtio_rng ;
AddFilesToPackage add-ons kernel busses scsi : ahci virtio_scsi ; AddFilesToPackage add-ons kernel busses scsi : ahci virtio_scsi ;
AddFilesToPackage add-ons kernel busses usb : <usb>uhci <usb>ohci <usb>ehci AddFilesToPackage add-ons kernel busses usb : <usb>uhci <usb>ohci <usb>ehci
<usb>xhci ; <usb>xhci ;
+7
View File
@@ -620,6 +620,13 @@ struct pci_module_info {
#define PCI_wireless_cellular_ethernet 0x41 #define PCI_wireless_cellular_ethernet 0x41
#define PCI_wireless_other 0x80 #define PCI_wireless_other 0x80
/* ---
values for the class_sub field for class_base = 0x10 (encryption decryption)
--- */
#define PCI_encryption_decryption_network_computing 0x00
#define PCI_encryption_decryption_entertainment 0x10
#define PCI_encryption_decryption_other 0x80
/* --- /* ---
values for the class_sub field for class_base = 0x11 (data acquisition) values for the class_sub field for class_base = 0x11 (data acquisition)
--- */ --- */
+4
View File
@@ -7,3 +7,7 @@ KernelAddon virtio_rng :
virtio_rng.cpp virtio_rng.cpp
VirtioRNGDevice.cpp VirtioRNGDevice.cpp
; ;
SubInclude HAIKU_TOP src add-ons kernel busses random ccp ;
@@ -0,0 +1,14 @@
SubDir HAIKU_TOP src add-ons kernel busses random ccp ;
UsePrivateHeaders drivers ;
UsePrivateKernelHeaders ;
SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) $(DOTDOT) $(DOTDOT) bus_managers acpi acpica include ] ;
SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) $(DOTDOT) $(DOTDOT) bus_managers acpi acpica include platform ] ;
KernelAddon ccp_rng :
ccp.cpp
ccp_acpi.cpp
ccp_pci.cpp
;
@@ -0,0 +1,159 @@
/*
* Copyright 2022, Jérôme Duval, [email protected].
* Distributed under the terms of the MIT License.
*/
#include <new>
#include <stdio.h>
#include <string.h>
#include <condition_variable.h>
#include <dpc.h>
#include "random.h"
#include "ccp.h"
#define CCP_REG_TRNG 0xc
device_manager_info* gDeviceManager;
random_for_controller_interface *gRandom;
dpc_module_info *gDPC;
static void
handleDPC(void *arg)
{
CALLED();
ccp_device_info* bus = reinterpret_cast<ccp_device_info*>(arg);
uint32 lowValue = read32(bus->registers + CCP_REG_TRNG);
uint32 highValue = read32(bus->registers + CCP_REG_TRNG);
if (lowValue == 0 || highValue == 0)
return;
gRandom->queue_randomness((uint64)lowValue | ((uint64)highValue << 32));
}
static int32
handleTimerHook(struct timer* timer)
{
ccp_device_info* bus = reinterpret_cast<ccp_device_info*>(timer->user_data);
gDPC->queue_dpc(bus->dpcHandle, handleDPC, bus);
return B_HANDLED_INTERRUPT;
}
// #pragma mark -
static status_t
register_device(device_node* parent)
{
device_attr attrs[] = {
{B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "CCP device"}},
{B_DEVICE_BUS, B_STRING_TYPE, {string: "CCP"}},
{}
};
return gDeviceManager->register_node(parent,
CCP_DEVICE_MODULE_NAME, attrs, NULL, NULL);
}
static status_t
init_bus(device_node* node, void** bus_cookie)
{
CALLED();
driver_module_info* driver;
ccp_device_info* bus;
device_node* parent = gDeviceManager->get_parent_node(node);
gDeviceManager->get_driver(parent, &driver, (void**)&bus);
gDeviceManager->put_node(parent);
TRACE_ALWAYS("init_bus() addr 0x%" B_PRIxPHYSADDR " size 0x%" B_PRIx64
" \n", bus->base_addr, bus->map_size);
bus->registersArea = map_physical_memory("CCP memory mapped registers",
bus->base_addr, bus->map_size, B_ANY_KERNEL_ADDRESS,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
(void **)&bus->registers);
if (bus->registersArea < 0)
return bus->registersArea;
status_t status = gDPC->new_dpc_queue(&bus->dpcHandle, "ccp timer",
B_LOW_PRIORITY);
if (status != B_OK) {
ERROR("dpc setup failed (%s)\n", strerror(status));
return status;
}
bus->extractTimer.user_data = bus;
status = add_timer(&bus->extractTimer, &handleTimerHook, 1 * 1000 * 1000, B_PERIODIC_TIMER);
if (status != B_OK) {
ERROR("timer setup failed (%s)\n", strerror(status));
return status;
}
// trigger also now
gDPC->queue_dpc(bus->dpcHandle, handleDPC, bus);
*bus_cookie = bus;
return B_OK;
}
static void
uninit_bus(void* bus_cookie)
{
ccp_device_info* bus = (ccp_device_info*)bus_cookie;
cancel_timer(&bus->extractTimer);
gDPC->delete_dpc_queue(&bus->dpcHandle);
if (bus->registersArea >= 0)
delete_area(bus->registersArea);
}
// #pragma mark -
module_dependency module_dependencies[] = {
{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager },
{ RANDOM_FOR_CONTROLLER_MODULE_NAME, (module_info**)&gRandom },
{ B_DPC_MODULE_NAME, (module_info **)&gDPC },
{}
};
static driver_module_info sCcpDeviceModule = {
{
CCP_DEVICE_MODULE_NAME,
0,
NULL
},
NULL, // supports device
register_device,
init_bus,
uninit_bus,
NULL, // register child devices
NULL, // rescan
NULL, // device removed
};
module_info* modules[] = {
(module_info* )&gCcpAcpiDevice,
(module_info* )&sCcpDeviceModule,
(module_info* )&gCcpPciDevice,
NULL
};
@@ -0,0 +1,59 @@
/*
* Copyright 2022, Jérôme Duval, [email protected].
*
* Distributed under the terms of the MIT License.
*/
#ifndef _CCP_H
#define _CCP_H
#include <stdlib.h>
#include <KernelExport.h>
//#define TRACE_CCP_RNG
#ifndef DRIVER_NAME
# define DRIVER_NAME "ccp_rng"
#endif
#ifdef TRACE_CCP_RNG
# define TRACE(x...) dprintf("\33[33m" DRIVER_NAME ":\33[0m " x)
#else
# define TRACE(x...) ;
#endif
#define TRACE_ALWAYS(x...) dprintf("\33[33m" DRIVER_NAME ":\33[0m " x)
#define ERROR(x...) dprintf("\33[33m" DRIVER_NAME ":\33[0m " x)
#define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
#define CCP_ACPI_DEVICE_MODULE_NAME "busses/random/ccp_rng/acpi/driver_v1"
#define CCP_PCI_DEVICE_MODULE_NAME "busses/random/ccp_rng/pci/driver_v1"
#define CCP_DEVICE_MODULE_NAME "busses/random/ccp_rng/device/v1"
#define read32(address) \
(*((volatile uint32*)(address)))
extern device_manager_info* gDeviceManager;
extern driver_module_info gCcpAcpiDevice;
extern driver_module_info gCcpPciDevice;
typedef struct {
phys_addr_t base_addr;
uint64 map_size;
device_node* node;
device_node* driver_node;
area_id registersArea;
addr_t registers;
timer extractTimer;
void* dpcHandle;
} ccp_device_info;
#endif // _CCP_H
@@ -0,0 +1,180 @@
/*
* Copyright 2022, Jérôme Duval, [email protected].
* Distributed under the terms of the MIT License.
*/
#include <new>
#include <stdio.h>
#include <string.h>
#include <ACPI.h>
#include <ByteOrder.h>
#include <condition_variable.h>
extern "C" {
# include "acpi.h"
}
#define DRIVER_NAME "ccp_rng_acpi"
#include "ccp.h"
typedef struct {
ccp_device_info info;
acpi_device_module_info* acpi;
acpi_device device;
} ccp_acpi_sim_info;
struct ccp_crs {
uint32 addr_bas;
uint32 addr_len;
};
static acpi_status
ccp_scan_parse_callback(ACPI_RESOURCE *res, void *context)
{
struct ccp_crs* crs = (struct ccp_crs*)context;
if (res->Type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) {
crs->addr_bas = res->Data.FixedMemory32.Address;
crs->addr_len = res->Data.FixedMemory32.AddressLength;
}
return B_OK;
}
// #pragma mark -
static status_t
init_device(device_node* node, void** device_cookie)
{
CALLED();
status_t status = B_OK;
ccp_acpi_sim_info* bus = (ccp_acpi_sim_info*)calloc(1,
sizeof(ccp_acpi_sim_info));
if (bus == NULL)
return B_NO_MEMORY;
acpi_device_module_info* acpi;
acpi_device device;
{
device_node* acpiParent = gDeviceManager->get_parent_node(node);
gDeviceManager->get_driver(acpiParent, (driver_module_info**)&acpi,
(void**)&device);
gDeviceManager->put_node(acpiParent);
}
bus->acpi = acpi;
bus->device = device;
struct ccp_crs crs;
status = acpi->walk_resources(device, (ACPI_STRING)"_CRS",
ccp_scan_parse_callback, &crs);
if (status != B_OK) {
ERROR("Error while getting resouces\n");
free(bus);
return status;
}
if (crs.addr_bas == 0 || crs.addr_len == 0) {
TRACE("skipping non configured CCP devices\n");
free(bus);
return B_BAD_VALUE;
}
bus->info.base_addr = crs.addr_bas;
bus->info.map_size = crs.addr_len;
*device_cookie = bus;
return B_OK;
}
static void
uninit_device(void* device_cookie)
{
ccp_acpi_sim_info* bus = (ccp_acpi_sim_info*)device_cookie;
free(bus);
}
static status_t
register_device(device_node* parent)
{
device_attr attrs[] = {
{B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "CCP ACPI"}},
{B_DEVICE_BUS, B_STRING_TYPE, {string: "CCP"}},
{B_DEVICE_FIXED_CHILD, B_STRING_TYPE, {string: CCP_DEVICE_MODULE_NAME}},
{}
};
return gDeviceManager->register_node(parent,
CCP_ACPI_DEVICE_MODULE_NAME, attrs, NULL, NULL);
}
static float
supports_device(device_node* parent)
{
const char* bus;
// make sure parent is a CCP ACPI device node
if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)
< B_OK) {
return -1;
}
if (strcmp(bus, "acpi") != 0)
return 0.0f;
// check whether it's really a device
uint32 device_type;
if (gDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM,
&device_type, false) != B_OK
|| device_type != ACPI_TYPE_DEVICE) {
return 0.0;
}
// check whether it's a CCP device
const char *name;
if (gDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &name,
false) != B_OK) {
return 0.0;
}
if (strcmp(name, "AMDI0C00") == 0) {
TRACE("CCP device found! name %s\n", name);
return 0.6f;
}
return 0.0f;
}
// #pragma mark -
driver_module_info gCcpAcpiDevice = {
{
CCP_ACPI_DEVICE_MODULE_NAME,
0,
NULL
},
supports_device,
register_device,
init_device,
uninit_device,
NULL, // register_child_devices,
NULL, // rescan
NULL, // device removed
};
@@ -0,0 +1,159 @@
/*
* Copyright 2022, Jérôme Duval, [email protected].
* Distributed under the terms of the MIT License.
*/
#include <new>
#include <stdio.h>
#include <string.h>
#include <ByteOrder.h>
#include <bus/PCI.h>
#include <PCI_x86.h>
#define DRIVER_NAME "ccp_rng_pci"
#include "ccp.h"
typedef struct {
ccp_device_info info;
pci_device_module_info* pci;
pci_device* device;
pci_info pciinfo;
} ccp_pci_sim_info;
// #pragma mark -
static status_t
init_device(device_node* node, void** device_cookie)
{
CALLED();
ccp_pci_sim_info* bus = (ccp_pci_sim_info*)calloc(1,
sizeof(ccp_pci_sim_info));
if (bus == NULL)
return B_NO_MEMORY;
pci_device_module_info* pci;
pci_device* device;
{
device_node* pciParent = gDeviceManager->get_parent_node(node);
gDeviceManager->get_driver(pciParent, (driver_module_info**)&pci,
(void**)&device);
gDeviceManager->put_node(pciParent);
}
bus->pci = pci;
bus->device = device;
pci_info *pciInfo = &bus->pciinfo;
pci->get_pci_info(device, pciInfo);
#define BAR_INDEX 2
bus->info.base_addr = pciInfo->u.h0.base_registers[BAR_INDEX];
bus->info.map_size = pciInfo->u.h0.base_register_sizes[BAR_INDEX];
if ((pciInfo->u.h0.base_register_flags[BAR_INDEX] & PCI_address_type)
== PCI_address_type_64) {
bus->info.base_addr |= (uint64)pciInfo->u.h0.base_registers[BAR_INDEX + 1] << 32;
bus->info.map_size |= (uint64)pciInfo->u.h0.base_register_sizes[BAR_INDEX + 1] << 32;
}
if (bus->info.base_addr == 0) {
ERROR("PCI BAR not assigned\n");
free(bus);
return B_ERROR;
}
// enable bus master and memory
uint16 pcicmd = pci->read_pci_config(device, PCI_command, 2);
pcicmd |= PCI_command_master | PCI_command_memory;
pci->write_pci_config(device, PCI_command, 2, pcicmd);
*device_cookie = bus;
return B_OK;
}
static void
uninit_device(void* device_cookie)
{
ccp_pci_sim_info* bus = (ccp_pci_sim_info*)device_cookie;
free(bus);
}
static status_t
register_device(device_node* parent)
{
device_attr attrs[] = {
{B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "CCP PCI"}},
{B_DEVICE_BUS, B_STRING_TYPE, {string: "CCP"}},
{B_DEVICE_FIXED_CHILD, B_STRING_TYPE, {string: CCP_DEVICE_MODULE_NAME}},
{}
};
return gDeviceManager->register_node(parent,
CCP_PCI_DEVICE_MODULE_NAME, attrs, NULL, NULL);
}
static float
supports_device(device_node* parent)
{
const char* bus;
uint16 vendorID, deviceID;
// make sure parent is a CCP PCI device node
if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)
< B_OK || gDeviceManager->get_attr_uint16(parent, B_DEVICE_VENDOR_ID,
&vendorID, false) < B_OK
|| gDeviceManager->get_attr_uint16(parent, B_DEVICE_ID, &deviceID,
false) < B_OK) {
return -1;
}
if (strcmp(bus, "pci") != 0)
return 0.0f;
if (vendorID != 0x1022)
return 0.0f;
switch (deviceID) {
case 0x1456:
case 0x1468:
case 0x1486:
case 0x1537:
case 0x15df:
break;
default:
return 0.0f;
}
#ifdef TRACE_CCP_RNG
TRACE("CCP RNG device found! vendor 0x%04x, device 0x%04x\n", vendorID, deviceID);
#endif
return 0.8f;
}
// #pragma mark -
driver_module_info gCcpPciDevice = {
{
CCP_PCI_DEVICE_MODULE_NAME,
0,
NULL
},
supports_device,
register_device,
init_device,
uninit_device,
NULL, // register_child_devices,
NULL, // rescan
NULL, // device removed
};
@@ -1727,6 +1727,16 @@ device_node::_GetNextDriverPath(void*& cookie, KPath& _path)
break; break;
} }
break; break;
case PCI_encryption_decryption:
switch (subType) {
case PCI_encryption_decryption_other:
_AddPath(*stack, "busses", "random");
break;
default:
_AddPath(*stack, "drivers");
break;
}
break;
case PCI_data_acquisition: case PCI_data_acquisition:
switch (subType) { switch (subType) {
case PCI_data_acquisition_other: case PCI_data_acquisition_other:
@@ -1874,7 +1884,14 @@ device_node::_AlwaysRegisterDynamic()
get_attr_uint16(this, B_DEVICE_TYPE, &type, false); get_attr_uint16(this, B_DEVICE_TYPE, &type, false);
get_attr_uint16(this, B_DEVICE_SUB_TYPE, &subType, false); get_attr_uint16(this, B_DEVICE_SUB_TYPE, &subType, false);
return type == PCI_serial_bus || type == PCI_bridge || type == 0; switch (type) {
case PCI_serial_bus:
case PCI_bridge:
case PCI_encryption_decryption:
case 0:
return true;
}
return false;
// TODO: we may want to be a bit more specific in the future // TODO: we may want to be a bit more specific in the future
} }