// SPDX-License-Identifier: GPL-2.0-only /* * AMD Cryptographic Coprocessor (CCP) driver * * Copyright (C) 2013,2019 Advanced Micro Devices, Inc. * * Author: Tom Lendacky <[email protected]> * Author: Gary R Hook <[email protected]> */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/kthread.h> #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/spinlock.h> #include <linux/spinlock_types.h> #include <linux/types.h> #include <linux/mutex.h> #include <linux/delay.h> #include <linux/hw_random.h> #include <linux/cpu.h> #include <linux/atomic.h> #ifdef CONFIG_X86 #include <asm/cpu_device_id.h> #endif #include <linux/ccp.h> #include "ccp-dev.h" #define MAX_CCPS … /* Limit CCP use to a specifed number of queues per device */ static unsigned int nqueues; module_param(nqueues, uint, 0444); MODULE_PARM_DESC(…) …; /* Limit the maximum number of configured CCPs */ static atomic_t dev_count = …; static unsigned int max_devs = …; module_param(max_devs, uint, 0444); MODULE_PARM_DESC(…) …; struct ccp_tasklet_data { … }; /* Human-readable error strings */ #define CCP_MAX_ERROR_CODE … static char *ccp_error_codes[] = …; void ccp_log_error(struct ccp_device *d, unsigned int e) { … } /* List of CCPs, CCP count, read-write access lock, and access functions * * Lock structure: get ccp_unit_lock for reading whenever we need to * examine the CCP list. While holding it for reading we can acquire * the RR lock to update the round-robin next-CCP pointer. The unit lock * must be acquired before the RR lock. * * If the unit-lock is acquired for writing, we have total control over * the list, so there's no value in getting the RR lock. */ static DEFINE_RWLOCK(ccp_unit_lock); static LIST_HEAD(ccp_units); /* Round-robin counter */ static DEFINE_SPINLOCK(ccp_rr_lock); static struct ccp_device *ccp_rr; /** * ccp_add_device - add a CCP device to the list * * @ccp: ccp_device struct pointer * * Put this CCP on the unit list, which makes it available * for use. * * Returns zero if a CCP device is present, -ENODEV otherwise. */ void ccp_add_device(struct ccp_device *ccp) { … } /** * ccp_del_device - remove a CCP device from the list * * @ccp: ccp_device struct pointer * * Remove this unit from the list of devices. If the next device * up for use is this one, adjust the pointer. If this is the last * device, NULL the pointer. */ void ccp_del_device(struct ccp_device *ccp) { … } int ccp_register_rng(struct ccp_device *ccp) { … } void ccp_unregister_rng(struct ccp_device *ccp) { … } static struct ccp_device *ccp_get_device(void) { … } /** * ccp_present - check if a CCP device is present * * Returns zero if a CCP device is present, -ENODEV otherwise. */ int ccp_present(void) { … } EXPORT_SYMBOL_GPL(…); /** * ccp_version - get the version of the CCP device * * Returns the version from the first unit on the list; * otherwise a zero if no CCP device is present */ unsigned int ccp_version(void) { … } EXPORT_SYMBOL_GPL(…); /** * ccp_enqueue_cmd - queue an operation for processing by the CCP * * @cmd: ccp_cmd struct to be processed * * Queue a cmd to be processed by the CCP. If queueing the cmd * would exceed the defined length of the cmd queue the cmd will * only be queued if the CCP_CMD_MAY_BACKLOG flag is set and will * result in a return code of -EBUSY. * * The callback routine specified in the ccp_cmd struct will be * called to notify the caller of completion (if the cmd was not * backlogged) or advancement out of the backlog. If the cmd has * advanced out of the backlog the "err" value of the callback * will be -EINPROGRESS. Any other "err" value during callback is * the result of the operation. * * The cmd has been successfully queued if: * the return code is -EINPROGRESS or * the return code is -EBUSY and CCP_CMD_MAY_BACKLOG flag is set */ int ccp_enqueue_cmd(struct ccp_cmd *cmd) { … } EXPORT_SYMBOL_GPL(…); static void ccp_do_cmd_backlog(struct work_struct *work) { … } static struct ccp_cmd *ccp_dequeue_cmd(struct ccp_cmd_queue *cmd_q) { … } static void ccp_do_cmd_complete(unsigned long data) { … } /** * ccp_cmd_queue_thread - create a kernel thread to manage a CCP queue * * @data: thread-specific data */ int ccp_cmd_queue_thread(void *data) { … } /** * ccp_alloc_struct - allocate and initialize the ccp_device struct * * @sp: sp_device struct of the CCP */ struct ccp_device *ccp_alloc_struct(struct sp_device *sp) { … } int ccp_trng_read(struct hwrng *rng, void *data, size_t max, bool wait) { … } bool ccp_queues_suspended(struct ccp_device *ccp) { … } void ccp_dev_suspend(struct sp_device *sp) { … } void ccp_dev_resume(struct sp_device *sp) { … } int ccp_dev_init(struct sp_device *sp) { … } void ccp_dev_destroy(struct sp_device *sp) { … }