| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * AMD Cryptographic Coprocessor (CCP) driver |
|---|
| 3 | 4 | * |
|---|
| 4 | | - * Copyright (C) 2013,2017 Advanced Micro Devices, Inc. |
|---|
| 5 | + * Copyright (C) 2013,2019 Advanced Micro Devices, Inc. |
|---|
| 5 | 6 | * |
|---|
| 6 | 7 | * Author: Tom Lendacky <thomas.lendacky@amd.com> |
|---|
| 7 | 8 | * Author: Gary R Hook <gary.hook@amd.com> |
|---|
| 8 | | - * |
|---|
| 9 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 10 | | - * it under the terms of the GNU General Public License version 2 as |
|---|
| 11 | | - * published by the Free Software Foundation. |
|---|
| 12 | 9 | */ |
|---|
| 13 | 10 | |
|---|
| 11 | +#include <linux/module.h> |
|---|
| 14 | 12 | #include <linux/kernel.h> |
|---|
| 15 | 13 | #include <linux/kthread.h> |
|---|
| 16 | 14 | #include <linux/sched.h> |
|---|
| .. | .. |
|---|
| 22 | 20 | #include <linux/delay.h> |
|---|
| 23 | 21 | #include <linux/hw_random.h> |
|---|
| 24 | 22 | #include <linux/cpu.h> |
|---|
| 23 | +#include <linux/atomic.h> |
|---|
| 25 | 24 | #ifdef CONFIG_X86 |
|---|
| 26 | 25 | #include <asm/cpu_device_id.h> |
|---|
| 27 | 26 | #endif |
|---|
| 28 | 27 | #include <linux/ccp.h> |
|---|
| 29 | 28 | |
|---|
| 30 | 29 | #include "ccp-dev.h" |
|---|
| 30 | + |
|---|
| 31 | +#define MAX_CCPS 32 |
|---|
| 32 | + |
|---|
| 33 | +/* Limit CCP use to a specifed number of queues per device */ |
|---|
| 34 | +static unsigned int nqueues = 0; |
|---|
| 35 | +module_param(nqueues, uint, 0444); |
|---|
| 36 | +MODULE_PARM_DESC(nqueues, "Number of queues per CCP (minimum 1; default: all available)"); |
|---|
| 37 | + |
|---|
| 38 | +/* Limit the maximum number of configured CCPs */ |
|---|
| 39 | +static atomic_t dev_count = ATOMIC_INIT(0); |
|---|
| 40 | +static unsigned int max_devs = MAX_CCPS; |
|---|
| 41 | +module_param(max_devs, uint, 0444); |
|---|
| 42 | +MODULE_PARM_DESC(max_devs, "Maximum number of CCPs to enable (default: all; 0 disables all CCPs)"); |
|---|
| 31 | 43 | |
|---|
| 32 | 44 | struct ccp_tasklet_data { |
|---|
| 33 | 45 | struct completion completion; |
|---|
| .. | .. |
|---|
| 519 | 531 | return len; |
|---|
| 520 | 532 | } |
|---|
| 521 | 533 | |
|---|
| 522 | | -#ifdef CONFIG_PM |
|---|
| 523 | 534 | bool ccp_queues_suspended(struct ccp_device *ccp) |
|---|
| 524 | 535 | { |
|---|
| 525 | 536 | unsigned int suspended = 0; |
|---|
| .. | .. |
|---|
| 537 | 548 | return ccp->cmd_q_count == suspended; |
|---|
| 538 | 549 | } |
|---|
| 539 | 550 | |
|---|
| 540 | | -int ccp_dev_suspend(struct sp_device *sp, pm_message_t state) |
|---|
| 551 | +int ccp_dev_suspend(struct sp_device *sp) |
|---|
| 541 | 552 | { |
|---|
| 542 | 553 | struct ccp_device *ccp = sp->ccp_data; |
|---|
| 543 | 554 | unsigned long flags; |
|---|
| .. | .. |
|---|
| 589 | 600 | |
|---|
| 590 | 601 | return 0; |
|---|
| 591 | 602 | } |
|---|
| 592 | | -#endif |
|---|
| 593 | 603 | |
|---|
| 594 | 604 | int ccp_dev_init(struct sp_device *sp) |
|---|
| 595 | 605 | { |
|---|
| .. | .. |
|---|
| 597 | 607 | struct ccp_device *ccp; |
|---|
| 598 | 608 | int ret; |
|---|
| 599 | 609 | |
|---|
| 610 | + /* |
|---|
| 611 | + * Check how many we have so far, and stop after reaching |
|---|
| 612 | + * that number |
|---|
| 613 | + */ |
|---|
| 614 | + if (atomic_inc_return(&dev_count) > max_devs) |
|---|
| 615 | + return 0; /* don't fail the load */ |
|---|
| 616 | + |
|---|
| 600 | 617 | ret = -ENOMEM; |
|---|
| 601 | 618 | ccp = ccp_alloc_struct(sp); |
|---|
| 602 | 619 | if (!ccp) |
|---|
| 603 | 620 | goto e_err; |
|---|
| 604 | 621 | sp->ccp_data = ccp; |
|---|
| 622 | + |
|---|
| 623 | + if (!nqueues || (nqueues > MAX_HW_QUEUES)) |
|---|
| 624 | + ccp->max_q_count = MAX_HW_QUEUES; |
|---|
| 625 | + else |
|---|
| 626 | + ccp->max_q_count = nqueues; |
|---|
| 605 | 627 | |
|---|
| 606 | 628 | ccp->vdata = (struct ccp_vdata *)sp->dev_vdata->ccp_vdata; |
|---|
| 607 | 629 | if (!ccp->vdata || !ccp->vdata->version) { |
|---|
| .. | .. |
|---|
| 617 | 639 | ccp->vdata->setup(ccp); |
|---|
| 618 | 640 | |
|---|
| 619 | 641 | ret = ccp->vdata->perform->init(ccp); |
|---|
| 620 | | - if (ret) |
|---|
| 642 | + if (ret) { |
|---|
| 643 | + /* A positive number means that the device cannot be initialized, |
|---|
| 644 | + * but no additional message is required. |
|---|
| 645 | + */ |
|---|
| 646 | + if (ret > 0) |
|---|
| 647 | + goto e_quiet; |
|---|
| 648 | + |
|---|
| 649 | + /* An unexpected problem occurred, and should be reported in the log */ |
|---|
| 621 | 650 | goto e_err; |
|---|
| 651 | + } |
|---|
| 622 | 652 | |
|---|
| 623 | 653 | dev_notice(dev, "ccp enabled\n"); |
|---|
| 624 | 654 | |
|---|
| 625 | 655 | return 0; |
|---|
| 626 | 656 | |
|---|
| 627 | 657 | e_err: |
|---|
| 628 | | - sp->ccp_data = NULL; |
|---|
| 629 | | - |
|---|
| 630 | 658 | dev_notice(dev, "ccp initialization failed\n"); |
|---|
| 631 | 659 | |
|---|
| 660 | +e_quiet: |
|---|
| 661 | + sp->ccp_data = NULL; |
|---|
| 662 | + |
|---|
| 632 | 663 | return ret; |
|---|
| 633 | 664 | } |
|---|
| 634 | 665 | |
|---|