diff options
Diffstat (limited to 'drivers/char')
144 files changed, 5539 insertions, 7373 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 8fb33c90482f..9865227af167 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -199,17 +199,6 @@ config NWFLASH source "drivers/char/hw_random/Kconfig" -config DTLK - tristate "Double Talk PC internal speech card support" - depends on ISA - help - This driver is for the DoubleTalk PC, a speech synthesizer - manufactured by RC Systems (<https://www.rcsys.com/>). It is also - called the `internal DoubleTalk'. - - To compile this driver as a module, choose M here: the - module will be called dtlk. - config XILINX_HWICAP tristate "Xilinx HWICAP Support" depends on MICROBLAZE @@ -220,24 +209,9 @@ config XILINX_HWICAP If unsure, say N. -config APPLICOM - tristate "Applicom intelligent fieldbus card support" - depends on PCI - help - This driver provides the kernel-side support for the intelligent - fieldbus cards made by Applicom International. More information - about these cards can be found on the WWW at the address - <https://www.applicom-int.com/>, or by email from David Woodhouse - <dwmw2@infradead.org>. - - To compile this driver as a module, choose M here: the - module will be called applicom. - - If unsure, say N. - config SONYPI tristate "Sony Vaio Programmable I/O Control Device support" - depends on X86_32 && PCI && INPUT + depends on X86_32 && PCI && INPUT && HAS_IOPORT depends on ACPI_EC || !ACPI help This driver enables access to the Sony Programmable I/O Control @@ -249,32 +223,6 @@ config SONYPI To compile this driver as a module, choose M here: the module will be called sonypi. -config MWAVE - tristate "ACP Modem (Mwave) support" - depends on X86 && TTY - select SERIAL_8250 - help - The ACP modem (Mwave) for Linux is a WinModem. It is composed of a - kernel driver and a user level application. Together these components - support direct attachment to public switched telephone networks (PSTNs) - and support selected world wide countries. - - This version of the ACP Modem driver supports the IBM Thinkpad 600E, - 600, and 770 that include on board ACP modem hardware. - - The modem also supports the standard communications port interface - (ttySx) and is compatible with the Hayes AT Command Set. - - The user level application needed to use this driver can be found at - the IBM Linux Technology Center (LTC) web site: - <http://www.ibm.com/linux/ltc/>. - - If you own one of the above IBM Thinkpads which has the Mwave chipset - in it, say Y. - - To compile this driver as a module, choose M here: the - module will be called mwave. - config SCx200_GPIO tristate "NatSemi SCx200 GPIO Support" depends on SCx200 @@ -404,7 +352,7 @@ config TELCLOCK configuration of the telecom clock configuration settings. This device is used for hardware synchronization across the ATCA backplane fabric. Upon loading, the driver exports a sysfs directory, - /sys/devices/platform/telco_clock, with a number of files for + /sys/devices/faux/telco_clock, with a number of files for controlling the behavior of this hardware. source "drivers/s390/char/Kconfig" diff --git a/drivers/char/Makefile b/drivers/char/Makefile index e9b360cdc99a..a46d7bf7c4c8 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -6,6 +6,7 @@ obj-y += mem.o random.o obj-$(CONFIG_TTY_PRINTK) += ttyprintk.o obj-y += misc.o +obj-$(CONFIG_TEST_MISC_MINOR) += misc_minor_kunit.o obj-$(CONFIG_ATARI_DSP56K) += dsp56k.o obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o obj-$(CONFIG_UV_MMTIMER) += uv_mmtimer.o @@ -15,8 +16,6 @@ obj-$(CONFIG_PRINTER) += lp.o obj-$(CONFIG_APM_EMULATION) += apm-emulation.o -obj-$(CONFIG_DTLK) += dtlk.o -obj-$(CONFIG_APPLICOM) += applicom.o obj-$(CONFIG_SONYPI) += sonypi.o obj-$(CONFIG_HPET) += hpet.o obj-$(CONFIG_XILINX_HWICAP) += xilinx_hwicap/ @@ -32,7 +31,6 @@ obj-$(CONFIG_PC8736x_GPIO) += pc8736x_gpio.o obj-$(CONFIG_NSC_GPIO) += nsc_gpio.o obj-$(CONFIG_TELCLOCK) += tlclk.o -obj-$(CONFIG_MWAVE) += mwave/ obj-y += agp/ obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o diff --git a/drivers/char/adi.c b/drivers/char/adi.c index f9bec10a6064..0849d933a2d5 100644 --- a/drivers/char/adi.c +++ b/drivers/char/adi.c @@ -80,8 +80,8 @@ static ssize_t adi_read(struct file *file, char __user *buf, bytes_read += ver_buf_sz; ver_buf_idx = 0; - ver_buf_sz = min(count - bytes_read, - (size_t)MAX_BUF_SZ); + ver_buf_sz = min_t(size_t, count - bytes_read, + MAX_BUF_SZ); } } @@ -131,7 +131,7 @@ static ssize_t adi_write(struct file *file, const char __user *buf, ssize_t ret; int i; - if (count <= 0) + if (count == 0) return -EINVAL; ver_buf_sz = min_t(size_t, count, MAX_BUF_SZ); @@ -157,7 +157,7 @@ static ssize_t adi_write(struct file *file, const char __user *buf, } bytes_written += ver_buf_sz; - ver_buf_sz = min(count - bytes_written, (size_t)MAX_BUF_SZ); + ver_buf_sz = min_t(size_t, count - bytes_written, MAX_BUF_SZ); } while (bytes_written < count); (*offp) += bytes_written; diff --git a/drivers/char/agp/amd-k7-agp.c b/drivers/char/agp/amd-k7-agp.c index 795c8c9ff680..898ff30ffd46 100644 --- a/drivers/char/agp/amd-k7-agp.c +++ b/drivers/char/agp/amd-k7-agp.c @@ -85,13 +85,12 @@ static int amd_create_gatt_pages(int nr_tables) int retval = 0; int i; - tables = kcalloc(nr_tables + 1, sizeof(struct amd_page_map *), - GFP_KERNEL); + tables = kzalloc_objs(struct amd_page_map *, nr_tables + 1); if (tables == NULL) return -ENOMEM; for (i = 0; i < nr_tables; i++) { - entry = kzalloc(sizeof(struct amd_page_map), GFP_KERNEL); + entry = kzalloc_obj(struct amd_page_map); tables[i] = entry; if (entry == NULL) { retval = -ENOMEM; diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c index 8e41731d3642..6741270e0a98 100644 --- a/drivers/char/agp/amd64-agp.c +++ b/drivers/char/agp/amd64-agp.c @@ -16,7 +16,7 @@ #include <linux/mmzone.h> #include <asm/page.h> /* PAGE_SIZE */ #include <asm/e820/api.h> -#include <asm/amd_nb.h> +#include <asm/amd/nb.h> #include <asm/gart.h> #include "agp.h" @@ -546,7 +546,7 @@ static int agp_amd64_probe(struct pci_dev *pdev, /* Fill in the mode register */ pci_read_config_dword(pdev, bridge->capndx+PCI_AGP_STATUS, &bridge->mode); - if (cache_nbs(pdev, cap_ptr) == -1) { + if (cache_nbs(pdev, cap_ptr) < 0) { agp_put_bridge(bridge); return -ENODEV; } @@ -720,11 +720,6 @@ static const struct pci_device_id agp_amd64_pci_table[] = { MODULE_DEVICE_TABLE(pci, agp_amd64_pci_table); -static const struct pci_device_id agp_amd64_pci_promisc_table[] = { - { PCI_DEVICE_CLASS(0, 0) }, - { } -}; - static DEFINE_SIMPLE_DEV_PM_OPS(agp_amd64_pm_ops, NULL, agp_amd64_resume); static struct pci_driver agp_amd64_pci_driver = { @@ -739,6 +734,7 @@ static struct pci_driver agp_amd64_pci_driver = { /* Not static due to IOMMU code calling it early. */ int __init agp_amd64_init(void) { + struct pci_dev *pdev = NULL; int err = 0; if (agp_off) @@ -767,9 +763,13 @@ int __init agp_amd64_init(void) } /* Look for any AGP bridge */ - agp_amd64_pci_driver.id_table = agp_amd64_pci_promisc_table; - err = driver_attach(&agp_amd64_pci_driver.driver); - if (err == 0 && agp_bridges_found == 0) { + for_each_pci_dev(pdev) + if (pci_find_capability(pdev, PCI_CAP_ID_AGP)) + pci_add_dynid(&agp_amd64_pci_driver, + pdev->vendor, pdev->device, + pdev->subsystem_vendor, + pdev->subsystem_device, 0, 0, 0); + if (agp_bridges_found == 0) { pci_unregister_driver(&agp_amd64_pci_driver); err = -ENODEV; } diff --git a/drivers/char/agp/ati-agp.c b/drivers/char/agp/ati-agp.c index f7871afe08cf..485b02fc9b18 100644 --- a/drivers/char/agp/ati-agp.c +++ b/drivers/char/agp/ati-agp.c @@ -112,13 +112,12 @@ static int ati_create_gatt_pages(int nr_tables) int retval = 0; int i; - tables = kcalloc(nr_tables + 1, sizeof(struct ati_page_map *), - GFP_KERNEL); + tables = kzalloc_objs(struct ati_page_map *, nr_tables + 1); if (tables == NULL) return -ENOMEM; for (i = 0; i < nr_tables; i++) { - entry = kzalloc(sizeof(struct ati_page_map), GFP_KERNEL); + entry = kzalloc_obj(struct ati_page_map); tables[i] = entry; if (entry == NULL) { retval = -ENOMEM; diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index 1776afd3ee07..8983addca695 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -238,7 +238,7 @@ struct agp_bridge_data *agp_alloc_bridge(void) { struct agp_bridge_data *bridge; - bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); + bridge = kzalloc_obj(*bridge); if (!bridge) return NULL; @@ -323,18 +323,6 @@ int agp_try_unsupported_boot; EXPORT_SYMBOL(agp_off); EXPORT_SYMBOL(agp_try_unsupported_boot); -static int __init agp_init(void) -{ - if (!agp_off) - printk(KERN_INFO "Linux agpgart interface v%d.%d\n", - AGPGART_VERSION_MAJOR, AGPGART_VERSION_MINOR); - return 0; -} - -static void __exit agp_exit(void) -{ -} - #ifndef MODULE static __init int agp_setup(char *s) { @@ -351,7 +339,3 @@ MODULE_AUTHOR("Dave Jones, Jeff Hartmann"); MODULE_DESCRIPTION("AGP GART driver"); MODULE_LICENSE("GPL and additional rights"); MODULE_ALIAS_MISCDEV(AGPGART_MINOR); - -module_init(agp_init); -module_exit(agp_exit); - diff --git a/drivers/char/agp/efficeon-agp.c b/drivers/char/agp/efficeon-agp.c index 0d25bbdc7e6a..4d0b7d7c0aad 100644 --- a/drivers/char/agp/efficeon-agp.c +++ b/drivers/char/agp/efficeon-agp.c @@ -27,6 +27,7 @@ #include <linux/gfp.h> #include <linux/page-flags.h> #include <linux/mm.h> +#include <asm/cpuid/api.h> #include "agp.h" #include "intel-agp.h" diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index 3ffbb1c80c5c..41752ed2b075 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -101,7 +101,7 @@ static struct agp_memory *agp_create_user_memory(unsigned long num_agp_pages) if (INT_MAX/sizeof(struct page *) < num_agp_pages) return NULL; - new = kzalloc(sizeof(struct agp_memory), GFP_KERNEL); + new = kzalloc_obj(struct agp_memory); if (new == NULL) return NULL; @@ -127,7 +127,7 @@ struct agp_memory *agp_create_memory(int scratch_pages) { struct agp_memory *new; - new = kzalloc(sizeof(struct agp_memory), GFP_KERNEL); + new = kzalloc_obj(struct agp_memory); if (new == NULL) return NULL; diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index ef30445527a2..bcc26785175d 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -53,6 +53,7 @@ struct intel_gtt_driver { * of the mmio register file, that's done in the generic code. */ void (*cleanup)(void); void (*write_entry)(dma_addr_t addr, unsigned int entry, unsigned int flags); + dma_addr_t (*read_entry)(unsigned int entry, bool *is_present, bool *is_local); /* Flags is a more or less chipset specific opaque value. * For chipsets that need to support old ums (non-gem) code, this * needs to be identical to the various supported agp memory types! */ @@ -336,6 +337,19 @@ static void i810_write_entry(dma_addr_t addr, unsigned int entry, writel_relaxed(addr | pte_flags, intel_private.gtt + entry); } +static dma_addr_t i810_read_entry(unsigned int entry, + bool *is_present, bool *is_local) +{ + u32 val; + + val = readl(intel_private.gtt + entry); + + *is_present = val & I810_PTE_VALID; + *is_local = val & I810_PTE_LOCAL; + + return val & ~0xfff; +} + static resource_size_t intel_gtt_stolen_size(void) { u16 gmch_ctrl; @@ -741,6 +755,19 @@ static void i830_write_entry(dma_addr_t addr, unsigned int entry, writel_relaxed(addr | pte_flags, intel_private.gtt + entry); } +static dma_addr_t i830_read_entry(unsigned int entry, + bool *is_present, bool *is_local) +{ + u32 val; + + val = readl(intel_private.gtt + entry); + + *is_present = val & I810_PTE_VALID; + *is_local = false; + + return val & ~0xfff; +} + bool intel_gmch_enable_gtt(void) { u8 __iomem *reg; @@ -878,6 +905,13 @@ void intel_gmch_gtt_insert_sg_entries(struct sg_table *st, } EXPORT_SYMBOL(intel_gmch_gtt_insert_sg_entries); +dma_addr_t intel_gmch_gtt_read_entry(unsigned int pg, + bool *is_present, bool *is_local) +{ + return intel_private.driver->read_entry(pg, is_present, is_local); +} +EXPORT_SYMBOL(intel_gmch_gtt_read_entry); + #if IS_ENABLED(CONFIG_AGP_INTEL) static void intel_gmch_gtt_insert_pages(unsigned int first_entry, unsigned int num_entries, @@ -1126,6 +1160,19 @@ static void i965_write_entry(dma_addr_t addr, writel_relaxed(addr | pte_flags, intel_private.gtt + entry); } +static dma_addr_t i965_read_entry(unsigned int entry, + bool *is_present, bool *is_local) +{ + u64 val; + + val = readl(intel_private.gtt + entry); + + *is_present = val & I810_PTE_VALID; + *is_local = false; + + return ((val & 0xf0) << 28) | (val & ~0xfff); +} + static int i9xx_setup(void) { phys_addr_t reg_addr; @@ -1187,6 +1234,7 @@ static const struct intel_gtt_driver i81x_gtt_driver = { .cleanup = i810_cleanup, .check_flags = i830_check_flags, .write_entry = i810_write_entry, + .read_entry = i810_read_entry, }; static const struct intel_gtt_driver i8xx_gtt_driver = { .gen = 2, @@ -1194,6 +1242,7 @@ static const struct intel_gtt_driver i8xx_gtt_driver = { .setup = i830_setup, .cleanup = i830_cleanup, .write_entry = i830_write_entry, + .read_entry = i830_read_entry, .dma_mask_size = 32, .check_flags = i830_check_flags, .chipset_flush = i830_chipset_flush, @@ -1205,6 +1254,7 @@ static const struct intel_gtt_driver i915_gtt_driver = { .cleanup = i9xx_cleanup, /* i945 is the last gpu to need phys mem (for overlay and cursors). */ .write_entry = i830_write_entry, + .read_entry = i830_read_entry, .dma_mask_size = 32, .check_flags = i830_check_flags, .chipset_flush = i9xx_chipset_flush, @@ -1215,6 +1265,7 @@ static const struct intel_gtt_driver g33_gtt_driver = { .setup = i9xx_setup, .cleanup = i9xx_cleanup, .write_entry = i965_write_entry, + .read_entry = i965_read_entry, .dma_mask_size = 36, .check_flags = i830_check_flags, .chipset_flush = i9xx_chipset_flush, @@ -1225,6 +1276,7 @@ static const struct intel_gtt_driver pineview_gtt_driver = { .setup = i9xx_setup, .cleanup = i9xx_cleanup, .write_entry = i965_write_entry, + .read_entry = i965_read_entry, .dma_mask_size = 36, .check_flags = i830_check_flags, .chipset_flush = i9xx_chipset_flush, @@ -1235,6 +1287,7 @@ static const struct intel_gtt_driver i965_gtt_driver = { .setup = i9xx_setup, .cleanup = i9xx_cleanup, .write_entry = i965_write_entry, + .read_entry = i965_read_entry, .dma_mask_size = 36, .check_flags = i830_check_flags, .chipset_flush = i9xx_chipset_flush, @@ -1244,6 +1297,7 @@ static const struct intel_gtt_driver g4x_gtt_driver = { .setup = i9xx_setup, .cleanup = i9xx_cleanup, .write_entry = i965_write_entry, + .read_entry = i965_read_entry, .dma_mask_size = 36, .check_flags = i830_check_flags, .chipset_flush = i9xx_chipset_flush, @@ -1254,6 +1308,7 @@ static const struct intel_gtt_driver ironlake_gtt_driver = { .setup = i9xx_setup, .cleanup = i9xx_cleanup, .write_entry = i965_write_entry, + .read_entry = i965_read_entry, .dma_mask_size = 36, .check_flags = i830_check_flags, .chipset_flush = i9xx_chipset_flush, diff --git a/drivers/char/agp/isoch.c b/drivers/char/agp/isoch.c index 7ecf20a6d19c..698182609b92 100644 --- a/drivers/char/agp/isoch.c +++ b/drivers/char/agp/isoch.c @@ -92,7 +92,7 @@ static int agp_3_5_isochronous_node_enable(struct agp_bridge_data *bridge, * We'll work with an array of isoch_data's (one for each * device in dev_list) throughout this function. */ - master = kmalloc_array(ndevs, sizeof(*master), GFP_KERNEL); + master = kmalloc_objs(*master, ndevs); if (master == NULL) { ret = -ENOMEM; goto get_out; @@ -333,7 +333,7 @@ int agp_3_5_enable(struct agp_bridge_data *bridge) * Allocate a head for our AGP 3.5 device list * (multiple AGP v3 devices are allowed behind a single bridge). */ - if ((dev_list = kmalloc(sizeof(*dev_list), GFP_KERNEL)) == NULL) { + if ((dev_list = kmalloc_obj(*dev_list)) == NULL) { ret = -ENOMEM; goto get_out; } @@ -362,7 +362,7 @@ int agp_3_5_enable(struct agp_bridge_data *bridge) case 0x0300: /* Display controller */ case 0x0400: /* Multimedia controller */ - if ((cur = kmalloc(sizeof(*cur), GFP_KERNEL)) == NULL) { + if ((cur = kmalloc_obj(*cur)) == NULL) { ret = -ENOMEM; goto free_and_exit; } diff --git a/drivers/char/agp/nvidia-agp.c b/drivers/char/agp/nvidia-agp.c index e424360fb4a1..4787391bb6b4 100644 --- a/drivers/char/agp/nvidia-agp.c +++ b/drivers/char/agp/nvidia-agp.c @@ -11,6 +11,7 @@ #include <linux/page-flags.h> #include <linux/mm.h> #include <linux/jiffies.h> +#include <asm/msr.h> #include "agp.h" /* NVIDIA registers */ diff --git a/drivers/char/agp/sworks-agp.c b/drivers/char/agp/sworks-agp.c index 0ab7562d17c9..8f6781c2701a 100644 --- a/drivers/char/agp/sworks-agp.c +++ b/drivers/char/agp/sworks-agp.c @@ -96,13 +96,12 @@ static int serverworks_create_gatt_pages(int nr_tables) int retval = 0; int i; - tables = kcalloc(nr_tables + 1, sizeof(struct serverworks_page_map *), - GFP_KERNEL); + tables = kzalloc_objs(struct serverworks_page_map *, nr_tables + 1); if (tables == NULL) return -ENOMEM; for (i = 0; i < nr_tables; i++) { - entry = kzalloc(sizeof(struct serverworks_page_map), GFP_KERNEL); + entry = kzalloc_obj(struct serverworks_page_map); if (entry == NULL) { retval = -ENOMEM; break; diff --git a/drivers/char/agp/uninorth-agp.c b/drivers/char/agp/uninorth-agp.c index b8d7115b8c9e..08175fe2f530 100644 --- a/drivers/char/agp/uninorth-agp.c +++ b/drivers/char/agp/uninorth-agp.c @@ -404,9 +404,7 @@ static int uninorth_create_gatt_table(struct agp_bridge_data *bridge) if (table == NULL) return -ENOMEM; - uninorth_priv.pages_arr = kmalloc_array(1 << page_order, - sizeof(struct page *), - GFP_KERNEL); + uninorth_priv.pages_arr = kmalloc_objs(struct page *, 1 << page_order); if (uninorth_priv.pages_arr == NULL) goto enomem; diff --git a/drivers/char/apm-emulation.c b/drivers/char/apm-emulation.c index e795390b070f..389874f44967 100644 --- a/drivers/char/apm-emulation.c +++ b/drivers/char/apm-emulation.c @@ -141,22 +141,11 @@ static struct apm_queue kapmd_queue; static DEFINE_MUTEX(state_lock); -static const char driver_version[] = "1.13"; /* no spaces */ - - - -/* - * Compatibility cruft until the IPAQ people move over to the new - * interface. - */ -static void __apm_get_power_status(struct apm_power_info *info) -{ -} /* * This allows machines to provide their own "apm get power status" function. */ -void (*apm_get_power_status)(struct apm_power_info *) = __apm_get_power_status; +void (*apm_get_power_status)(struct apm_power_info *); EXPORT_SYMBOL(apm_get_power_status); @@ -354,7 +343,7 @@ static int apm_open(struct inode * inode, struct file * filp) { struct apm_user *as; - as = kzalloc(sizeof(*as), GFP_KERNEL); + as = kzalloc_obj(*as); if (as) { /* * XXX - this is a tiny bit broken, when we consider BSD @@ -435,6 +424,8 @@ static struct miscdevice apm_device = { */ static int proc_apm_show(struct seq_file *m, void *v) { + static const char driver_version[] = "1.13"; /* no spaces */ + struct apm_power_info info; char *units; diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c deleted file mode 100644 index 9fed9706d9cd..000000000000 --- a/drivers/char/applicom.c +++ /dev/null @@ -1,854 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Derived from Applicom driver ac.c for SCO Unix */ -/* Ported by David Woodhouse, Axiom (Cambridge) Ltd. */ -/* dwmw2@infradead.org 30/8/98 */ -/* $Id: ac.c,v 1.30 2000/03/22 16:03:57 dwmw2 Exp $ */ -/* This module is for Linux 2.1 and 2.2 series kernels. */ -/*****************************************************************************/ -/* J PAGET 18/02/94 passage V2.4.2 ioctl avec code 2 reset to les interrupt */ -/* ceci pour reseter correctement apres une sortie sauvage */ -/* J PAGET 02/05/94 passage V2.4.3 dans le traitement de d'interruption, */ -/* LoopCount n'etait pas initialise a 0. */ -/* F LAFORSE 04/07/95 version V2.6.0 lecture bidon apres acces a une carte */ -/* pour liberer le bus */ -/* J.PAGET 19/11/95 version V2.6.1 Nombre, addresse,irq n'est plus configure */ -/* et passe en argument a acinit, mais est scrute sur le bus pour s'adapter */ -/* au nombre de cartes presentes sur le bus. IOCL code 6 affichait V2.4.3 */ -/* F.LAFORSE 28/11/95 creation de fichiers acXX.o avec les differentes */ -/* addresses de base des cartes, IOCTL 6 plus complet */ -/* J.PAGET le 19/08/96 copie de la version V2.6 en V2.8.0 sans modification */ -/* de code autre que le texte V2.6.1 en V2.8.0 */ -/*****************************************************************************/ - - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/sched/signal.h> -#include <linux/slab.h> -#include <linux/errno.h> -#include <linux/mutex.h> -#include <linux/miscdevice.h> -#include <linux/pci.h> -#include <linux/wait.h> -#include <linux/init.h> -#include <linux/fs.h> -#include <linux/nospec.h> - -#include <asm/io.h> -#include <linux/uaccess.h> - -#include "applicom.h" - - -/* NOTE: We use for loops with {write,read}b() instead of - memcpy_{from,to}io throughout this driver. This is because - the board doesn't correctly handle word accesses - only - bytes. -*/ - - -#undef DEBUG - -#define MAX_BOARD 8 /* maximum of pc board possible */ -#define MAX_ISA_BOARD 4 -#define LEN_RAM_IO 0x800 - -#ifndef PCI_VENDOR_ID_APPLICOM -#define PCI_VENDOR_ID_APPLICOM 0x1389 -#define PCI_DEVICE_ID_APPLICOM_PCIGENERIC 0x0001 -#define PCI_DEVICE_ID_APPLICOM_PCI2000IBS_CAN 0x0002 -#define PCI_DEVICE_ID_APPLICOM_PCI2000PFB 0x0003 -#endif - -static DEFINE_MUTEX(ac_mutex); -static char *applicom_pci_devnames[] = { - "PCI board", - "PCI2000IBS / PCI2000CAN", - "PCI2000PFB" -}; - -static const struct pci_device_id applicom_pci_tbl[] = { - { PCI_VDEVICE(APPLICOM, PCI_DEVICE_ID_APPLICOM_PCIGENERIC) }, - { PCI_VDEVICE(APPLICOM, PCI_DEVICE_ID_APPLICOM_PCI2000IBS_CAN) }, - { PCI_VDEVICE(APPLICOM, PCI_DEVICE_ID_APPLICOM_PCI2000PFB) }, - { 0 } -}; -MODULE_DEVICE_TABLE(pci, applicom_pci_tbl); - -MODULE_AUTHOR("David Woodhouse & Applicom International"); -MODULE_DESCRIPTION("Driver for Applicom Profibus card"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(AC_MINOR); - -static struct applicom_board { - unsigned long PhysIO; - void __iomem *RamIO; - wait_queue_head_t FlagSleepSend; - long irq; - spinlock_t mutex; -} apbs[MAX_BOARD]; - -static unsigned int irq; /* interrupt number IRQ */ -static unsigned long mem; /* physical segment of board */ - -module_param_hw(irq, uint, irq, 0); -MODULE_PARM_DESC(irq, "IRQ of the Applicom board"); -module_param_hw(mem, ulong, iomem, 0); -MODULE_PARM_DESC(mem, "Shared Memory Address of Applicom board"); - -static unsigned int numboards; /* number of installed boards */ -static volatile unsigned char Dummy; -static DECLARE_WAIT_QUEUE_HEAD(FlagSleepRec); -static unsigned int WriteErrorCount; /* number of write error */ -static unsigned int ReadErrorCount; /* number of read error */ -static unsigned int DeviceErrorCount; /* number of device error */ - -static ssize_t ac_read (struct file *, char __user *, size_t, loff_t *); -static ssize_t ac_write (struct file *, const char __user *, size_t, loff_t *); -static long ac_ioctl(struct file *, unsigned int, unsigned long); -static irqreturn_t ac_interrupt(int, void *); - -static const struct file_operations ac_fops = { - .owner = THIS_MODULE, - .read = ac_read, - .write = ac_write, - .unlocked_ioctl = ac_ioctl, -}; - -static struct miscdevice ac_miscdev = { - AC_MINOR, - "ac", - &ac_fops -}; - -static int dummy; /* dev_id for request_irq() */ - -static int ac_register_board(unsigned long physloc, void __iomem *loc, - unsigned char boardno) -{ - volatile unsigned char byte_reset_it; - - if((readb(loc + CONF_END_TEST) != 0x00) || - (readb(loc + CONF_END_TEST + 1) != 0x55) || - (readb(loc + CONF_END_TEST + 2) != 0xAA) || - (readb(loc + CONF_END_TEST + 3) != 0xFF)) - return 0; - - if (!boardno) - boardno = readb(loc + NUMCARD_OWNER_TO_PC); - - if (!boardno || boardno > MAX_BOARD) { - printk(KERN_WARNING "Board #%d (at 0x%lx) is out of range (1 <= x <= %d).\n", - boardno, physloc, MAX_BOARD); - return 0; - } - - if (apbs[boardno - 1].RamIO) { - printk(KERN_WARNING "Board #%d (at 0x%lx) conflicts with previous board #%d (at 0x%lx)\n", - boardno, physloc, boardno, apbs[boardno-1].PhysIO); - return 0; - } - - boardno--; - - apbs[boardno].PhysIO = physloc; - apbs[boardno].RamIO = loc; - init_waitqueue_head(&apbs[boardno].FlagSleepSend); - spin_lock_init(&apbs[boardno].mutex); - byte_reset_it = readb(loc + RAM_IT_TO_PC); - - numboards++; - return boardno + 1; -} - -static void __exit applicom_exit(void) -{ - unsigned int i; - - misc_deregister(&ac_miscdev); - - for (i = 0; i < MAX_BOARD; i++) { - - if (!apbs[i].RamIO) - continue; - - if (apbs[i].irq) - free_irq(apbs[i].irq, &dummy); - - iounmap(apbs[i].RamIO); - } -} - -static int __init applicom_init(void) -{ - int i, numisa = 0; - struct pci_dev *dev = NULL; - void __iomem *RamIO; - int boardno, ret; - - printk(KERN_INFO "Applicom driver: $Id: ac.c,v 1.30 2000/03/22 16:03:57 dwmw2 Exp $\n"); - - /* No mem and irq given - check for a PCI card */ - - while ( (dev = pci_get_class(PCI_CLASS_OTHERS << 16, dev))) { - - if (!pci_match_id(applicom_pci_tbl, dev)) - continue; - - if (pci_enable_device(dev)) { - pci_dev_put(dev); - return -EIO; - } - - RamIO = ioremap(pci_resource_start(dev, 0), LEN_RAM_IO); - - if (!RamIO) { - printk(KERN_INFO "ac.o: Failed to ioremap PCI memory " - "space at 0x%llx\n", - (unsigned long long)pci_resource_start(dev, 0)); - pci_disable_device(dev); - pci_dev_put(dev); - return -EIO; - } - - printk(KERN_INFO "Applicom %s found at mem 0x%llx, irq %d\n", - applicom_pci_devnames[dev->device-1], - (unsigned long long)pci_resource_start(dev, 0), - dev->irq); - - boardno = ac_register_board(pci_resource_start(dev, 0), - RamIO, 0); - if (!boardno) { - printk(KERN_INFO "ac.o: PCI Applicom device doesn't have correct signature.\n"); - iounmap(RamIO); - pci_disable_device(dev); - continue; - } - - if (request_irq(dev->irq, &ac_interrupt, IRQF_SHARED, "Applicom PCI", &dummy)) { - printk(KERN_INFO "Could not allocate IRQ %d for PCI Applicom device.\n", dev->irq); - iounmap(RamIO); - pci_disable_device(dev); - apbs[boardno - 1].RamIO = NULL; - continue; - } - - /* Enable interrupts. */ - - writeb(0x40, apbs[boardno - 1].RamIO + RAM_IT_FROM_PC); - - apbs[boardno - 1].irq = dev->irq; - } - - /* Finished with PCI cards. If none registered, - * and there was no mem/irq specified, exit */ - - if (!mem || !irq) { - if (numboards) - goto fin; - else { - printk(KERN_INFO "ac.o: No PCI boards found.\n"); - printk(KERN_INFO "ac.o: For an ISA board you must supply memory and irq parameters.\n"); - return -ENXIO; - } - } - - /* Now try the specified ISA cards */ - - for (i = 0; i < MAX_ISA_BOARD; i++) { - RamIO = ioremap(mem + (LEN_RAM_IO * i), LEN_RAM_IO); - - if (!RamIO) { - printk(KERN_INFO "ac.o: Failed to ioremap the ISA card's memory space (slot #%d)\n", i + 1); - continue; - } - - if (!(boardno = ac_register_board((unsigned long)mem+ (LEN_RAM_IO*i), - RamIO,i+1))) { - iounmap(RamIO); - continue; - } - - printk(KERN_NOTICE "Applicom ISA card found at mem 0x%lx, irq %d\n", mem + (LEN_RAM_IO*i), irq); - - if (!numisa) { - if (request_irq(irq, &ac_interrupt, IRQF_SHARED, "Applicom ISA", &dummy)) { - printk(KERN_WARNING "Could not allocate IRQ %d for ISA Applicom device.\n", irq); - iounmap(RamIO); - apbs[boardno - 1].RamIO = NULL; - } - else - apbs[boardno - 1].irq = irq; - } - else - apbs[boardno - 1].irq = 0; - - numisa++; - } - - if (!numisa) - printk(KERN_WARNING "ac.o: No valid ISA Applicom boards found " - "at mem 0x%lx\n", mem); - - fin: - init_waitqueue_head(&FlagSleepRec); - - WriteErrorCount = 0; - ReadErrorCount = 0; - DeviceErrorCount = 0; - - if (numboards) { - ret = misc_register(&ac_miscdev); - if (ret) { - printk(KERN_WARNING "ac.o: Unable to register misc device\n"); - goto out; - } - for (i = 0; i < MAX_BOARD; i++) { - int serial; - char boardname[(SERIAL_NUMBER - TYPE_CARD) + 1]; - - if (!apbs[i].RamIO) - continue; - - for (serial = 0; serial < SERIAL_NUMBER - TYPE_CARD; serial++) - boardname[serial] = readb(apbs[i].RamIO + TYPE_CARD + serial); - - boardname[serial] = 0; - - - printk(KERN_INFO "Applicom board %d: %s, PROM V%d.%d", - i+1, boardname, - (int)(readb(apbs[i].RamIO + VERS) >> 4), - (int)(readb(apbs[i].RamIO + VERS) & 0xF)); - - serial = (readb(apbs[i].RamIO + SERIAL_NUMBER) << 16) + - (readb(apbs[i].RamIO + SERIAL_NUMBER + 1) << 8) + - (readb(apbs[i].RamIO + SERIAL_NUMBER + 2) ); - - if (serial != 0) - printk(" S/N %d\n", serial); - else - printk("\n"); - } - return 0; - } - - else - return -ENXIO; - -out: - for (i = 0; i < MAX_BOARD; i++) { - if (!apbs[i].RamIO) - continue; - if (apbs[i].irq) - free_irq(apbs[i].irq, &dummy); - iounmap(apbs[i].RamIO); - } - return ret; -} - -module_init(applicom_init); -module_exit(applicom_exit); - - -static ssize_t ac_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) -{ - unsigned int NumCard; /* Board number 1 -> 8 */ - unsigned int IndexCard; /* Index board number 0 -> 7 */ - unsigned char TicCard; /* Board TIC to send */ - unsigned long flags; /* Current priority */ - struct st_ram_io st_loc; - struct mailbox tmpmailbox; -#ifdef DEBUG - int c; -#endif - DECLARE_WAITQUEUE(wait, current); - - if (count != sizeof(struct st_ram_io) + sizeof(struct mailbox)) { - static int warncount = 5; - if (warncount) { - printk(KERN_INFO "Hmmm. write() of Applicom card, length %zd != expected %zd\n", - count, sizeof(struct st_ram_io) + sizeof(struct mailbox)); - warncount--; - } - return -EINVAL; - } - - if(copy_from_user(&st_loc, buf, sizeof(struct st_ram_io))) - return -EFAULT; - - if(copy_from_user(&tmpmailbox, &buf[sizeof(struct st_ram_io)], - sizeof(struct mailbox))) - return -EFAULT; - - NumCard = st_loc.num_card; /* board number to send */ - TicCard = st_loc.tic_des_from_pc; /* tic number to send */ - IndexCard = NumCard - 1; - - if (IndexCard >= MAX_BOARD) - return -EINVAL; - IndexCard = array_index_nospec(IndexCard, MAX_BOARD); - - if (!apbs[IndexCard].RamIO) - return -EINVAL; - -#ifdef DEBUG - printk("Write to applicom card #%d. struct st_ram_io follows:", - IndexCard+1); - - for (c = 0; c < sizeof(struct st_ram_io);) { - - printk("\n%5.5X: %2.2X", c, ((unsigned char *) &st_loc)[c]); - - for (c++; c % 8 && c < sizeof(struct st_ram_io); c++) { - printk(" %2.2X", ((unsigned char *) &st_loc)[c]); - } - } - - printk("\nstruct mailbox follows:"); - - for (c = 0; c < sizeof(struct mailbox);) { - printk("\n%5.5X: %2.2X", c, ((unsigned char *) &tmpmailbox)[c]); - - for (c++; c % 8 && c < sizeof(struct mailbox); c++) { - printk(" %2.2X", ((unsigned char *) &tmpmailbox)[c]); - } - } - - printk("\n"); -#endif - - spin_lock_irqsave(&apbs[IndexCard].mutex, flags); - - /* Test octet ready correct */ - if(readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY) > 2) { - Dummy = readb(apbs[IndexCard].RamIO + VERS); - spin_unlock_irqrestore(&apbs[IndexCard].mutex, flags); - printk(KERN_WARNING "APPLICOM driver write error board %d, DataFromPcReady = %d\n", - IndexCard,(int)readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY)); - DeviceErrorCount++; - return -EIO; - } - - /* Place ourselves on the wait queue */ - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&apbs[IndexCard].FlagSleepSend, &wait); - - /* Check whether the card is ready for us */ - while (readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY) != 0) { - Dummy = readb(apbs[IndexCard].RamIO + VERS); - /* It's busy. Sleep. */ - - spin_unlock_irqrestore(&apbs[IndexCard].mutex, flags); - schedule(); - if (signal_pending(current)) { - remove_wait_queue(&apbs[IndexCard].FlagSleepSend, - &wait); - return -EINTR; - } - spin_lock_irqsave(&apbs[IndexCard].mutex, flags); - set_current_state(TASK_INTERRUPTIBLE); - } - - /* We may not have actually slept */ - set_current_state(TASK_RUNNING); - remove_wait_queue(&apbs[IndexCard].FlagSleepSend, &wait); - - writeb(1, apbs[IndexCard].RamIO + DATA_FROM_PC_READY); - - /* Which is best - lock down the pages with rawio and then - copy directly, or use bounce buffers? For now we do the latter - because it works with 2.2 still */ - { - unsigned char *from = (unsigned char *) &tmpmailbox; - void __iomem *to = apbs[IndexCard].RamIO + RAM_FROM_PC; - int c; - - for (c = 0; c < sizeof(struct mailbox); c++) - writeb(*(from++), to++); - } - - writeb(0x20, apbs[IndexCard].RamIO + TIC_OWNER_FROM_PC); - writeb(0xff, apbs[IndexCard].RamIO + NUMCARD_OWNER_FROM_PC); - writeb(TicCard, apbs[IndexCard].RamIO + TIC_DES_FROM_PC); - writeb(NumCard, apbs[IndexCard].RamIO + NUMCARD_DES_FROM_PC); - writeb(2, apbs[IndexCard].RamIO + DATA_FROM_PC_READY); - writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC); - Dummy = readb(apbs[IndexCard].RamIO + VERS); - spin_unlock_irqrestore(&apbs[IndexCard].mutex, flags); - return 0; -} - -static int do_ac_read(int IndexCard, char __user *buf, - struct st_ram_io *st_loc, struct mailbox *mailbox) -{ - void __iomem *from = apbs[IndexCard].RamIO + RAM_TO_PC; - unsigned char *to = (unsigned char *)mailbox; -#ifdef DEBUG - int c; -#endif - - st_loc->tic_owner_to_pc = readb(apbs[IndexCard].RamIO + TIC_OWNER_TO_PC); - st_loc->numcard_owner_to_pc = readb(apbs[IndexCard].RamIO + NUMCARD_OWNER_TO_PC); - - - { - int c; - - for (c = 0; c < sizeof(struct mailbox); c++) - *(to++) = readb(from++); - } - writeb(1, apbs[IndexCard].RamIO + ACK_FROM_PC_READY); - writeb(1, apbs[IndexCard].RamIO + TYP_ACK_FROM_PC); - writeb(IndexCard+1, apbs[IndexCard].RamIO + NUMCARD_ACK_FROM_PC); - writeb(readb(apbs[IndexCard].RamIO + TIC_OWNER_TO_PC), - apbs[IndexCard].RamIO + TIC_ACK_FROM_PC); - writeb(2, apbs[IndexCard].RamIO + ACK_FROM_PC_READY); - writeb(0, apbs[IndexCard].RamIO + DATA_TO_PC_READY); - writeb(2, apbs[IndexCard].RamIO + RAM_IT_FROM_PC); - Dummy = readb(apbs[IndexCard].RamIO + VERS); - -#ifdef DEBUG - printk("Read from applicom card #%d. struct st_ram_io follows:", NumCard); - - for (c = 0; c < sizeof(struct st_ram_io);) { - printk("\n%5.5X: %2.2X", c, ((unsigned char *)st_loc)[c]); - - for (c++; c % 8 && c < sizeof(struct st_ram_io); c++) { - printk(" %2.2X", ((unsigned char *)st_loc)[c]); - } - } - - printk("\nstruct mailbox follows:"); - - for (c = 0; c < sizeof(struct mailbox);) { - printk("\n%5.5X: %2.2X", c, ((unsigned char *)mailbox)[c]); - - for (c++; c % 8 && c < sizeof(struct mailbox); c++) { - printk(" %2.2X", ((unsigned char *)mailbox)[c]); - } - } - printk("\n"); -#endif - return (sizeof(struct st_ram_io) + sizeof(struct mailbox)); -} - -static ssize_t ac_read (struct file *filp, char __user *buf, size_t count, loff_t *ptr) -{ - unsigned long flags; - unsigned int i; - unsigned char tmp; - int ret = 0; - DECLARE_WAITQUEUE(wait, current); -#ifdef DEBUG - int loopcount=0; -#endif - /* No need to ratelimit this. Only root can trigger it anyway */ - if (count != sizeof(struct st_ram_io) + sizeof(struct mailbox)) { - printk( KERN_WARNING "Hmmm. read() of Applicom card, length %zd != expected %zd\n", - count,sizeof(struct st_ram_io) + sizeof(struct mailbox)); - return -EINVAL; - } - - while(1) { - /* Stick ourself on the wait queue */ - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&FlagSleepRec, &wait); - - /* Scan each board, looking for one which has a packet for us */ - for (i=0; i < MAX_BOARD; i++) { - if (!apbs[i].RamIO) - continue; - spin_lock_irqsave(&apbs[i].mutex, flags); - - tmp = readb(apbs[i].RamIO + DATA_TO_PC_READY); - - if (tmp == 2) { - struct st_ram_io st_loc; - struct mailbox mailbox; - - /* Got a packet for us */ - memset(&st_loc, 0, sizeof(st_loc)); - ret = do_ac_read(i, buf, &st_loc, &mailbox); - spin_unlock_irqrestore(&apbs[i].mutex, flags); - set_current_state(TASK_RUNNING); - remove_wait_queue(&FlagSleepRec, &wait); - - if (copy_to_user(buf, &st_loc, sizeof(st_loc))) - return -EFAULT; - if (copy_to_user(buf + sizeof(st_loc), &mailbox, sizeof(mailbox))) - return -EFAULT; - return tmp; - } - - if (tmp > 2) { - /* Got an error */ - Dummy = readb(apbs[i].RamIO + VERS); - - spin_unlock_irqrestore(&apbs[i].mutex, flags); - set_current_state(TASK_RUNNING); - remove_wait_queue(&FlagSleepRec, &wait); - - printk(KERN_WARNING "APPLICOM driver read error board %d, DataToPcReady = %d\n", - i,(int)readb(apbs[i].RamIO + DATA_TO_PC_READY)); - DeviceErrorCount++; - return -EIO; - } - - /* Nothing for us. Try the next board */ - Dummy = readb(apbs[i].RamIO + VERS); - spin_unlock_irqrestore(&apbs[i].mutex, flags); - - } /* per board */ - - /* OK - No boards had data for us. Sleep now */ - - schedule(); - remove_wait_queue(&FlagSleepRec, &wait); - - if (signal_pending(current)) - return -EINTR; - -#ifdef DEBUG - if (loopcount++ > 2) { - printk(KERN_DEBUG "Looping in ac_read. loopcount %d\n", loopcount); - } -#endif - } -} - -static irqreturn_t ac_interrupt(int vec, void *dev_instance) -{ - unsigned int i; - unsigned int FlagInt; - unsigned int LoopCount; - int handled = 0; - - // printk("Applicom interrupt on IRQ %d occurred\n", vec); - - LoopCount = 0; - - do { - FlagInt = 0; - for (i = 0; i < MAX_BOARD; i++) { - - /* Skip if this board doesn't exist */ - if (!apbs[i].RamIO) - continue; - - spin_lock(&apbs[i].mutex); - - /* Skip if this board doesn't want attention */ - if(readb(apbs[i].RamIO + RAM_IT_TO_PC) == 0) { - spin_unlock(&apbs[i].mutex); - continue; - } - - handled = 1; - FlagInt = 1; - writeb(0, apbs[i].RamIO + RAM_IT_TO_PC); - - if (readb(apbs[i].RamIO + DATA_TO_PC_READY) > 2) { - printk(KERN_WARNING "APPLICOM driver interrupt err board %d, DataToPcReady = %d\n", - i+1,(int)readb(apbs[i].RamIO + DATA_TO_PC_READY)); - DeviceErrorCount++; - } - - if((readb(apbs[i].RamIO + DATA_FROM_PC_READY) > 2) && - (readb(apbs[i].RamIO + DATA_FROM_PC_READY) != 6)) { - - printk(KERN_WARNING "APPLICOM driver interrupt err board %d, DataFromPcReady = %d\n", - i+1,(int)readb(apbs[i].RamIO + DATA_FROM_PC_READY)); - DeviceErrorCount++; - } - - if (readb(apbs[i].RamIO + DATA_TO_PC_READY) == 2) { /* mailbox sent by the card ? */ - if (waitqueue_active(&FlagSleepRec)) { - wake_up_interruptible(&FlagSleepRec); - } - } - - if (readb(apbs[i].RamIO + DATA_FROM_PC_READY) == 0) { /* ram i/o free for write by pc ? */ - if (waitqueue_active(&apbs[i].FlagSleepSend)) { /* process sleep during read ? */ - wake_up_interruptible(&apbs[i].FlagSleepSend); - } - } - Dummy = readb(apbs[i].RamIO + VERS); - - if(readb(apbs[i].RamIO + RAM_IT_TO_PC)) { - /* There's another int waiting on this card */ - spin_unlock(&apbs[i].mutex); - i--; - } else { - spin_unlock(&apbs[i].mutex); - } - } - if (FlagInt) - LoopCount = 0; - else - LoopCount++; - } while(LoopCount < 2); - return IRQ_RETVAL(handled); -} - - - -static long ac_ioctl(struct file *file, unsigned int cmd, unsigned long arg) - -{ /* @ ADG ou ATO selon le cas */ - int i; - unsigned char IndexCard; - void __iomem *pmem; - int ret = 0; - static int warncount = 10; - volatile unsigned char byte_reset_it; - struct st_ram_io *adgl; - void __user *argp = (void __user *)arg; - - /* In general, the device is only openable by root anyway, so we're not - particularly concerned that bogus ioctls can flood the console. */ - - adgl = memdup_user(argp, sizeof(struct st_ram_io)); - if (IS_ERR(adgl)) - return PTR_ERR(adgl); - - mutex_lock(&ac_mutex); - IndexCard = adgl->num_card-1; - - if (cmd != 6 && IndexCard >= MAX_BOARD) - goto err; - IndexCard = array_index_nospec(IndexCard, MAX_BOARD); - - if (cmd != 6 && !apbs[IndexCard].RamIO) - goto err; - - switch (cmd) { - - case 0: - pmem = apbs[IndexCard].RamIO; - for (i = 0; i < sizeof(struct st_ram_io); i++) - ((unsigned char *)adgl)[i]=readb(pmem++); - if (copy_to_user(argp, adgl, sizeof(struct st_ram_io))) - ret = -EFAULT; - break; - case 1: - pmem = apbs[IndexCard].RamIO + CONF_END_TEST; - for (i = 0; i < 4; i++) - adgl->conf_end_test[i] = readb(pmem++); - for (i = 0; i < 2; i++) - adgl->error_code[i] = readb(pmem++); - for (i = 0; i < 4; i++) - adgl->parameter_error[i] = readb(pmem++); - pmem = apbs[IndexCard].RamIO + VERS; - adgl->vers = readb(pmem); - pmem = apbs[IndexCard].RamIO + TYPE_CARD; - for (i = 0; i < 20; i++) - adgl->reserv1[i] = readb(pmem++); - *(int *)&adgl->reserv1[20] = - (readb(apbs[IndexCard].RamIO + SERIAL_NUMBER) << 16) + - (readb(apbs[IndexCard].RamIO + SERIAL_NUMBER + 1) << 8) + - (readb(apbs[IndexCard].RamIO + SERIAL_NUMBER + 2) ); - - if (copy_to_user(argp, adgl, sizeof(struct st_ram_io))) - ret = -EFAULT; - break; - case 2: - pmem = apbs[IndexCard].RamIO + CONF_END_TEST; - for (i = 0; i < 10; i++) - writeb(0xff, pmem++); - writeb(adgl->data_from_pc_ready, - apbs[IndexCard].RamIO + DATA_FROM_PC_READY); - - writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC); - - for (i = 0; i < MAX_BOARD; i++) { - if (apbs[i].RamIO) { - byte_reset_it = readb(apbs[i].RamIO + RAM_IT_TO_PC); - } - } - break; - case 3: - pmem = apbs[IndexCard].RamIO + TIC_DES_FROM_PC; - writeb(adgl->tic_des_from_pc, pmem); - break; - case 4: - pmem = apbs[IndexCard].RamIO + TIC_OWNER_TO_PC; - adgl->tic_owner_to_pc = readb(pmem++); - adgl->numcard_owner_to_pc = readb(pmem); - if (copy_to_user(argp, adgl,sizeof(struct st_ram_io))) - ret = -EFAULT; - break; - case 5: - writeb(adgl->num_card, apbs[IndexCard].RamIO + NUMCARD_OWNER_TO_PC); - writeb(adgl->num_card, apbs[IndexCard].RamIO + NUMCARD_DES_FROM_PC); - writeb(adgl->num_card, apbs[IndexCard].RamIO + NUMCARD_ACK_FROM_PC); - writeb(4, apbs[IndexCard].RamIO + DATA_FROM_PC_READY); - writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC); - break; - case 6: - printk(KERN_INFO "APPLICOM driver release .... V2.8.0 ($Revision: 1.30 $)\n"); - printk(KERN_INFO "Number of installed boards . %d\n", (int) numboards); - printk(KERN_INFO "Segment of board ........... %X\n", (int) mem); - printk(KERN_INFO "Interrupt IRQ number ....... %d\n", (int) irq); - for (i = 0; i < MAX_BOARD; i++) { - int serial; - char boardname[(SERIAL_NUMBER - TYPE_CARD) + 1]; - - if (!apbs[i].RamIO) - continue; - - for (serial = 0; serial < SERIAL_NUMBER - TYPE_CARD; serial++) - boardname[serial] = readb(apbs[i].RamIO + TYPE_CARD + serial); - boardname[serial] = 0; - - printk(KERN_INFO "Prom version board %d ....... V%d.%d %s", - i+1, - (int)(readb(apbs[i].RamIO + VERS) >> 4), - (int)(readb(apbs[i].RamIO + VERS) & 0xF), - boardname); - - - serial = (readb(apbs[i].RamIO + SERIAL_NUMBER) << 16) + - (readb(apbs[i].RamIO + SERIAL_NUMBER + 1) << 8) + - (readb(apbs[i].RamIO + SERIAL_NUMBER + 2) ); - - if (serial != 0) - printk(" S/N %d\n", serial); - else - printk("\n"); - } - if (DeviceErrorCount != 0) - printk(KERN_INFO "DeviceErrorCount ........... %d\n", DeviceErrorCount); - if (ReadErrorCount != 0) - printk(KERN_INFO "ReadErrorCount ............. %d\n", ReadErrorCount); - if (WriteErrorCount != 0) - printk(KERN_INFO "WriteErrorCount ............ %d\n", WriteErrorCount); - if (waitqueue_active(&FlagSleepRec)) - printk(KERN_INFO "Process in read pending\n"); - for (i = 0; i < MAX_BOARD; i++) { - if (apbs[i].RamIO && waitqueue_active(&apbs[i].FlagSleepSend)) - printk(KERN_INFO "Process in write pending board %d\n",i+1); - } - break; - default: - ret = -ENOTTY; - break; - } - Dummy = readb(apbs[IndexCard].RamIO + VERS); - kfree(adgl); - mutex_unlock(&ac_mutex); - return ret; - -err: - if (warncount) { - pr_warn("APPLICOM driver IOCTL, bad board number %d\n", - (int)IndexCard + 1); - warncount--; - } - kfree(adgl); - mutex_unlock(&ac_mutex); - return -EINVAL; - -} - diff --git a/drivers/char/applicom.h b/drivers/char/applicom.h deleted file mode 100644 index 282e08f159d5..000000000000 --- a/drivers/char/applicom.h +++ /dev/null @@ -1,86 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* $Id: applicom.h,v 1.2 1999/08/28 15:09:49 dwmw2 Exp $ */ - - -#ifndef __LINUX_APPLICOM_H__ -#define __LINUX_APPLICOM_H__ - - -#define DATA_TO_PC_READY 0x00 -#define TIC_OWNER_TO_PC 0x01 -#define NUMCARD_OWNER_TO_PC 0x02 -#define TIC_DES_TO_PC 0x03 -#define NUMCARD_DES_TO_PC 0x04 -#define DATA_FROM_PC_READY 0x05 -#define TIC_OWNER_FROM_PC 0x06 -#define NUMCARD_OWNER_FROM_PC 0x07 -#define TIC_DES_FROM_PC 0x08 -#define NUMCARD_DES_FROM_PC 0x09 -#define ACK_FROM_PC_READY 0x0E -#define TIC_ACK_FROM_PC 0x0F -#define NUMCARD_ACK_FROM_PC 0x010 -#define TYP_ACK_FROM_PC 0x011 -#define CONF_END_TEST 0x012 -#define ERROR_CODE 0x016 -#define PARAMETER_ERROR 0x018 -#define VERS 0x01E -#define RAM_TO_PC 0x040 -#define RAM_FROM_PC 0x0170 -#define TYPE_CARD 0x03C0 -#define SERIAL_NUMBER 0x03DA -#define RAM_IT_FROM_PC 0x03FE -#define RAM_IT_TO_PC 0x03FF - -struct mailbox{ - u16 stjb_codef; /* offset 00 */ - s16 stjb_status; /* offset 02 */ - u16 stjb_ticuser_root; /* offset 04 */ - u8 stjb_piduser[4]; /* offset 06 */ - u16 stjb_mode; /* offset 0A */ - u16 stjb_time; /* offset 0C */ - u16 stjb_stop; /* offset 0E */ - u16 stjb_nfonc; /* offset 10 */ - u16 stjb_ncard; /* offset 12 */ - u16 stjb_nchan; /* offset 14 */ - u16 stjb_nes; /* offset 16 */ - u16 stjb_nb; /* offset 18 */ - u16 stjb_typvar; /* offset 1A */ - u32 stjb_adr; /* offset 1C */ - u16 stjb_ticuser_dispcyc; /* offset 20 */ - u16 stjb_ticuser_protocol; /* offset 22 */ - u8 stjb_filler[12]; /* offset 24 */ - u8 stjb_data[256]; /* offset 30 */ - }; - -struct st_ram_io -{ - unsigned char data_to_pc_ready; - unsigned char tic_owner_to_pc; - unsigned char numcard_owner_to_pc; - unsigned char tic_des_to_pc; - unsigned char numcard_des_to_pc; - unsigned char data_from_pc_ready; - unsigned char tic_owner_from_pc; - unsigned char numcard_owner_from_pc; - unsigned char tic_des_from_pc; - unsigned char numcard_des_from_pc; - unsigned char ack_to_pc_ready; - unsigned char tic_ack_to_pc; - unsigned char numcard_ack_to_pc; - unsigned char typ_ack_to_pc; - unsigned char ack_from_pc_ready; - unsigned char tic_ack_from_pc; - unsigned char numcard_ack_from_pc; - unsigned char typ_ack_from_pc; - unsigned char conf_end_test[4]; - unsigned char error_code[2]; - unsigned char parameter_error[4]; - unsigned char time_base; - unsigned char nul_inc; - unsigned char vers; - unsigned char num_card; - unsigned char reserv1[32]; -}; - - -#endif /* __LINUX_APPLICOM_H__ */ diff --git a/drivers/char/bsr.c b/drivers/char/bsr.c index 837109ef6766..98e9327bf65d 100644 --- a/drivers/char/bsr.c +++ b/drivers/char/bsr.c @@ -186,8 +186,7 @@ static int bsr_add_node(struct device_node *bn) num_bsr_devs = bsr_bytes_len / sizeof(u32); for (i = 0 ; i < num_bsr_devs; i++) { - struct bsr_dev *cur = kzalloc(sizeof(struct bsr_dev), - GFP_KERNEL); + struct bsr_dev *cur = kzalloc_obj(struct bsr_dev); struct resource res; int result; diff --git a/drivers/char/dtlk.c b/drivers/char/dtlk.c deleted file mode 100644 index 27f5f9d19531..000000000000 --- a/drivers/char/dtlk.c +++ /dev/null @@ -1,663 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* -*- linux-c -*- - * dtlk.c - DoubleTalk PC driver for Linux - * - * Original author: Chris Pallotta <chris@allmedia.com> - * Current maintainer: Jim Van Zandt <jrv@vanzandt.mv.com> - * - * 2000-03-18 Jim Van Zandt: Fix polling. - * Eliminate dtlk_timer_active flag and separate dtlk_stop_timer - * function. Don't restart timer in dtlk_timer_tick. Restart timer - * in dtlk_poll after every poll. dtlk_poll returns mask (duh). - * Eliminate unused function dtlk_write_byte. Misc. code cleanups. - */ - -/* This driver is for the DoubleTalk PC, a speech synthesizer - manufactured by RC Systems (http://www.rcsys.com/). It was written - based on documentation in their User's Manual file and Developer's - Tools disk. - - The DoubleTalk PC contains four voice synthesizers: text-to-speech - (TTS), linear predictive coding (LPC), PCM/ADPCM, and CVSD. It - also has a tone generator. Output data for LPC are written to the - LPC port, and output data for the other modes are written to the - TTS port. - - Two kinds of data can be read from the DoubleTalk: status - information (in response to the "\001?" interrogation command) is - read from the TTS port, and index markers (which mark the progress - of the speech) are read from the LPC port. Not all models of the - DoubleTalk PC implement index markers. Both the TTS and LPC ports - can also display status flags. - - The DoubleTalk PC generates no interrupts. - - These characteristics are mapped into the Unix stream I/O model as - follows: - - "write" sends bytes to the TTS port. It is the responsibility of - the user program to switch modes among TTS, PCM/ADPCM, and CVSD. - This driver was written for use with the text-to-speech - synthesizer. If LPC output is needed some day, other minor device - numbers can be used to select among output modes. - - "read" gets index markers from the LPC port. If the device does - not implement index markers, the read will fail with error EINVAL. - - Status information is available using the DTLK_INTERROGATE ioctl. - - */ - -#include <linux/module.h> - -#define KERNEL -#include <linux/types.h> -#include <linux/fs.h> -#include <linux/mm.h> -#include <linux/errno.h> /* for -EBUSY */ -#include <linux/ioport.h> /* for request_region */ -#include <linux/delay.h> /* for loops_per_jiffy */ -#include <linux/sched.h> -#include <linux/mutex.h> -#include <asm/io.h> /* for inb_p, outb_p, inb, outb, etc. */ -#include <linux/uaccess.h> /* for get_user, etc. */ -#include <linux/wait.h> /* for wait_queue */ -#include <linux/init.h> /* for __init, module_{init,exit} */ -#include <linux/poll.h> /* for EPOLLIN, etc. */ -#include <linux/dtlk.h> /* local header file for DoubleTalk values */ - -#ifdef TRACING -#define TRACE_TEXT(str) printk(str); -#define TRACE_RET printk(")") -#else /* !TRACING */ -#define TRACE_TEXT(str) ((void) 0) -#define TRACE_RET ((void) 0) -#endif /* TRACING */ - -static DEFINE_MUTEX(dtlk_mutex); -static void dtlk_timer_tick(struct timer_list *unused); - -static int dtlk_major; -static int dtlk_port_lpc; -static int dtlk_port_tts; -static int dtlk_busy; -static int dtlk_has_indexing; -static unsigned int dtlk_portlist[] = -{0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0}; -static wait_queue_head_t dtlk_process_list; -static DEFINE_TIMER(dtlk_timer, dtlk_timer_tick); - -/* prototypes for file_operations struct */ -static ssize_t dtlk_read(struct file *, char __user *, - size_t nbytes, loff_t * ppos); -static ssize_t dtlk_write(struct file *, const char __user *, - size_t nbytes, loff_t * ppos); -static __poll_t dtlk_poll(struct file *, poll_table *); -static int dtlk_open(struct inode *, struct file *); -static int dtlk_release(struct inode *, struct file *); -static long dtlk_ioctl(struct file *file, - unsigned int cmd, unsigned long arg); - -static const struct file_operations dtlk_fops = -{ - .owner = THIS_MODULE, - .read = dtlk_read, - .write = dtlk_write, - .poll = dtlk_poll, - .unlocked_ioctl = dtlk_ioctl, - .open = dtlk_open, - .release = dtlk_release, -}; - -/* local prototypes */ -static int dtlk_dev_probe(void); -static struct dtlk_settings *dtlk_interrogate(void); -static int dtlk_readable(void); -static char dtlk_read_lpc(void); -static char dtlk_read_tts(void); -static int dtlk_writeable(void); -static char dtlk_write_bytes(const char *buf, int n); -static char dtlk_write_tts(char); -/* - static void dtlk_handle_error(char, char, unsigned int); - */ - -static ssize_t dtlk_read(struct file *file, char __user *buf, - size_t count, loff_t * ppos) -{ - unsigned int minor = iminor(file_inode(file)); - char ch; - int i = 0, retries; - - TRACE_TEXT("(dtlk_read"); - /* printk("DoubleTalk PC - dtlk_read()\n"); */ - - if (minor != DTLK_MINOR || !dtlk_has_indexing) - return -EINVAL; - - for (retries = 0; retries < loops_per_jiffy; retries++) { - while (i < count && dtlk_readable()) { - ch = dtlk_read_lpc(); - /* printk("dtlk_read() reads 0x%02x\n", ch); */ - if (put_user(ch, buf++)) - return -EFAULT; - i++; - } - if (i) - return i; - if (file->f_flags & O_NONBLOCK) - break; - msleep_interruptible(100); - } - if (retries == loops_per_jiffy) - printk(KERN_ERR "dtlk_read times out\n"); - TRACE_RET; - return -EAGAIN; -} - -static ssize_t dtlk_write(struct file *file, const char __user *buf, - size_t count, loff_t * ppos) -{ - int i = 0, retries = 0, ch; - - TRACE_TEXT("(dtlk_write"); -#ifdef TRACING - printk(" \""); - { - int i, ch; - for (i = 0; i < count; i++) { - if (get_user(ch, buf + i)) - return -EFAULT; - if (' ' <= ch && ch <= '~') - printk("%c", ch); - else - printk("\\%03o", ch); - } - printk("\""); - } -#endif - - if (iminor(file_inode(file)) != DTLK_MINOR) - return -EINVAL; - - while (1) { - while (i < count && !get_user(ch, buf) && - (ch == DTLK_CLEAR || dtlk_writeable())) { - dtlk_write_tts(ch); - buf++; - i++; - if (i % 5 == 0) - /* We yield our time until scheduled - again. This reduces the transfer - rate to 500 bytes/sec, but that's - still enough to keep up with the - speech synthesizer. */ - msleep_interruptible(1); - else { - /* the RDY bit goes zero 2-3 usec - after writing, and goes 1 again - 180-190 usec later. Here, we wait - up to 250 usec for the RDY bit to - go nonzero. */ - for (retries = 0; - retries < loops_per_jiffy / (4000/HZ); - retries++) - if (inb_p(dtlk_port_tts) & - TTS_WRITABLE) - break; - } - retries = 0; - } - if (i == count) - return i; - if (file->f_flags & O_NONBLOCK) - break; - - msleep_interruptible(1); - - if (++retries > 10 * HZ) { /* wait no more than 10 sec - from last write */ - printk("dtlk: write timeout. " - "inb_p(dtlk_port_tts) = 0x%02x\n", - inb_p(dtlk_port_tts)); - TRACE_RET; - return -EBUSY; - } - } - TRACE_RET; - return -EAGAIN; -} - -static __poll_t dtlk_poll(struct file *file, poll_table * wait) -{ - __poll_t mask = 0; - unsigned long expires; - - TRACE_TEXT(" dtlk_poll"); - /* - static long int j; - printk("."); - printk("<%ld>", jiffies-j); - j=jiffies; - */ - poll_wait(file, &dtlk_process_list, wait); - - if (dtlk_has_indexing && dtlk_readable()) { - del_timer(&dtlk_timer); - mask = EPOLLIN | EPOLLRDNORM; - } - if (dtlk_writeable()) { - del_timer(&dtlk_timer); - mask |= EPOLLOUT | EPOLLWRNORM; - } - /* there are no exception conditions */ - - /* There won't be any interrupts, so we set a timer instead. */ - expires = jiffies + 3*HZ / 100; - mod_timer(&dtlk_timer, expires); - - return mask; -} - -static void dtlk_timer_tick(struct timer_list *unused) -{ - TRACE_TEXT(" dtlk_timer_tick"); - wake_up_interruptible(&dtlk_process_list); -} - -static long dtlk_ioctl(struct file *file, - unsigned int cmd, - unsigned long arg) -{ - char __user *argp = (char __user *)arg; - struct dtlk_settings *sp; - char portval; - TRACE_TEXT(" dtlk_ioctl"); - - switch (cmd) { - - case DTLK_INTERROGATE: - mutex_lock(&dtlk_mutex); - sp = dtlk_interrogate(); - mutex_unlock(&dtlk_mutex); - if (copy_to_user(argp, sp, sizeof(struct dtlk_settings))) - return -EINVAL; - return 0; - - case DTLK_STATUS: - portval = inb_p(dtlk_port_tts); - return put_user(portval, argp); - - default: - return -EINVAL; - } -} - -/* Note that nobody ever sets dtlk_busy... */ -static int dtlk_open(struct inode *inode, struct file *file) -{ - TRACE_TEXT("(dtlk_open"); - - switch (iminor(inode)) { - case DTLK_MINOR: - if (dtlk_busy) - return -EBUSY; - return stream_open(inode, file); - - default: - return -ENXIO; - } -} - -static int dtlk_release(struct inode *inode, struct file *file) -{ - TRACE_TEXT("(dtlk_release"); - - switch (iminor(inode)) { - case DTLK_MINOR: - break; - - default: - break; - } - TRACE_RET; - - del_timer_sync(&dtlk_timer); - - return 0; -} - -static int __init dtlk_init(void) -{ - int err; - - dtlk_port_lpc = 0; - dtlk_port_tts = 0; - dtlk_busy = 0; - dtlk_major = register_chrdev(0, "dtlk", &dtlk_fops); - if (dtlk_major < 0) { - printk(KERN_ERR "DoubleTalk PC - cannot register device\n"); - return dtlk_major; - } - err = dtlk_dev_probe(); - if (err) { - unregister_chrdev(dtlk_major, "dtlk"); - return err; - } - printk(", MAJOR %d\n", dtlk_major); - - init_waitqueue_head(&dtlk_process_list); - - return 0; -} - -static void __exit dtlk_cleanup (void) -{ - dtlk_write_bytes("goodbye", 8); - msleep_interruptible(500); /* nap 0.50 sec but - could be awakened - earlier by - signals... */ - - dtlk_write_tts(DTLK_CLEAR); - unregister_chrdev(dtlk_major, "dtlk"); - release_region(dtlk_port_lpc, DTLK_IO_EXTENT); -} - -module_init(dtlk_init); -module_exit(dtlk_cleanup); - -/* ------------------------------------------------------------------------ */ - -static int dtlk_readable(void) -{ -#ifdef TRACING - printk(" dtlk_readable=%u@%u", inb_p(dtlk_port_lpc) != 0x7f, jiffies); -#endif - return inb_p(dtlk_port_lpc) != 0x7f; -} - -static int dtlk_writeable(void) -{ - /* TRACE_TEXT(" dtlk_writeable"); */ -#ifdef TRACINGMORE - printk(" dtlk_writeable=%u", (inb_p(dtlk_port_tts) & TTS_WRITABLE)!=0); -#endif - return inb_p(dtlk_port_tts) & TTS_WRITABLE; -} - -static int __init dtlk_dev_probe(void) -{ - unsigned int testval = 0; - int i = 0; - struct dtlk_settings *sp; - - if (dtlk_port_lpc | dtlk_port_tts) - return -EBUSY; - - for (i = 0; dtlk_portlist[i]; i++) { -#if 0 - printk("DoubleTalk PC - Port %03x = %04x\n", - dtlk_portlist[i], (testval = inw_p(dtlk_portlist[i]))); -#endif - - if (!request_region(dtlk_portlist[i], DTLK_IO_EXTENT, - "dtlk")) - continue; - testval = inw_p(dtlk_portlist[i]); - if ((testval &= 0xfbff) == 0x107f) { - dtlk_port_lpc = dtlk_portlist[i]; - dtlk_port_tts = dtlk_port_lpc + 1; - - sp = dtlk_interrogate(); - printk("DoubleTalk PC at %03x-%03x, " - "ROM version %s, serial number %u", - dtlk_portlist[i], dtlk_portlist[i] + - DTLK_IO_EXTENT - 1, - sp->rom_version, sp->serial_number); - - /* put LPC port into known state, so - dtlk_readable() gives valid result */ - outb_p(0xff, dtlk_port_lpc); - - /* INIT string and index marker */ - dtlk_write_bytes("\036\1@\0\0012I\r", 8); - /* posting an index takes 18 msec. Here, we - wait up to 100 msec to see whether it - appears. */ - msleep_interruptible(100); - dtlk_has_indexing = dtlk_readable(); -#ifdef TRACING - printk(", indexing %d\n", dtlk_has_indexing); -#endif -#ifdef INSCOPE - { -/* This macro records ten samples read from the LPC port, for later display */ -#define LOOK \ -for (i = 0; i < 10; i++) \ - { \ - buffer[b++] = inb_p(dtlk_port_lpc); \ - __delay(loops_per_jiffy/(1000000/HZ)); \ - } - char buffer[1000]; - int b = 0, i, j; - - LOOK - outb_p(0xff, dtlk_port_lpc); - buffer[b++] = 0; - LOOK - dtlk_write_bytes("\0012I\r", 4); - buffer[b++] = 0; - __delay(50 * loops_per_jiffy / (1000/HZ)); - outb_p(0xff, dtlk_port_lpc); - buffer[b++] = 0; - LOOK - - printk("\n"); - for (j = 0; j < b; j++) - printk(" %02x", buffer[j]); - printk("\n"); - } -#endif /* INSCOPE */ - -#ifdef OUTSCOPE - { -/* This macro records ten samples read from the TTS port, for later display */ -#define LOOK \ -for (i = 0; i < 10; i++) \ - { \ - buffer[b++] = inb_p(dtlk_port_tts); \ - __delay(loops_per_jiffy/(1000000/HZ)); /* 1 us */ \ - } - char buffer[1000]; - int b = 0, i, j; - - mdelay(10); /* 10 ms */ - LOOK - outb_p(0x03, dtlk_port_tts); - buffer[b++] = 0; - LOOK - LOOK - - printk("\n"); - for (j = 0; j < b; j++) - printk(" %02x", buffer[j]); - printk("\n"); - } -#endif /* OUTSCOPE */ - - dtlk_write_bytes("Double Talk found", 18); - - return 0; - } - release_region(dtlk_portlist[i], DTLK_IO_EXTENT); - } - - printk(KERN_INFO "DoubleTalk PC - not found\n"); - return -ENODEV; -} - -/* - static void dtlk_handle_error(char op, char rc, unsigned int minor) - { - printk(KERN_INFO"\nDoubleTalk PC - MINOR: %d, OPCODE: %d, ERROR: %d\n", - minor, op, rc); - return; - } - */ - -/* interrogate the DoubleTalk PC and return its settings */ -static struct dtlk_settings *dtlk_interrogate(void) -{ - unsigned char *t; - static char buf[sizeof(struct dtlk_settings) + 1]; - int total, i; - static struct dtlk_settings status; - TRACE_TEXT("(dtlk_interrogate"); - dtlk_write_bytes("\030\001?", 3); - for (total = 0, i = 0; i < 50; i++) { - buf[total] = dtlk_read_tts(); - if (total > 2 && buf[total] == 0x7f) - break; - if (total < sizeof(struct dtlk_settings)) - total++; - } - /* - if (i==50) printk("interrogate() read overrun\n"); - for (i=0; i<sizeof(buf); i++) - printk(" %02x", buf[i]); - printk("\n"); - */ - t = buf; - status.serial_number = t[0] + t[1] * 256; /* serial number is - little endian */ - t += 2; - - i = 0; - while (*t != '\r') { - status.rom_version[i] = *t; - if (i < sizeof(status.rom_version) - 1) - i++; - t++; - } - status.rom_version[i] = 0; - t++; - - status.mode = *t++; - status.punc_level = *t++; - status.formant_freq = *t++; - status.pitch = *t++; - status.speed = *t++; - status.volume = *t++; - status.tone = *t++; - status.expression = *t++; - status.ext_dict_loaded = *t++; - status.ext_dict_status = *t++; - status.free_ram = *t++; - status.articulation = *t++; - status.reverb = *t++; - status.eob = *t++; - status.has_indexing = dtlk_has_indexing; - TRACE_RET; - return &status; -} - -static char dtlk_read_tts(void) -{ - int portval, retries = 0; - char ch; - TRACE_TEXT("(dtlk_read_tts"); - - /* verify DT is ready, read char, wait for ACK */ - do { - portval = inb_p(dtlk_port_tts); - } while ((portval & TTS_READABLE) == 0 && - retries++ < DTLK_MAX_RETRIES); - if (retries > DTLK_MAX_RETRIES) - printk(KERN_ERR "dtlk_read_tts() timeout\n"); - - ch = inb_p(dtlk_port_tts); /* input from TTS port */ - ch &= 0x7f; - outb_p(ch, dtlk_port_tts); - - retries = 0; - do { - portval = inb_p(dtlk_port_tts); - } while ((portval & TTS_READABLE) != 0 && - retries++ < DTLK_MAX_RETRIES); - if (retries > DTLK_MAX_RETRIES) - printk(KERN_ERR "dtlk_read_tts() timeout\n"); - - TRACE_RET; - return ch; -} - -static char dtlk_read_lpc(void) -{ - int retries = 0; - char ch; - TRACE_TEXT("(dtlk_read_lpc"); - - /* no need to test -- this is only called when the port is readable */ - - ch = inb_p(dtlk_port_lpc); /* input from LPC port */ - - outb_p(0xff, dtlk_port_lpc); - - /* acknowledging a read takes 3-4 - usec. Here, we wait up to 20 usec - for the acknowledgement */ - retries = (loops_per_jiffy * 20) / (1000000/HZ); - while (inb_p(dtlk_port_lpc) != 0x7f && --retries > 0); - if (retries == 0) - printk(KERN_ERR "dtlk_read_lpc() timeout\n"); - - TRACE_RET; - return ch; -} - -/* write n bytes to tts port */ -static char dtlk_write_bytes(const char *buf, int n) -{ - char val = 0; - /* printk("dtlk_write_bytes(\"%-*s\", %d)\n", n, buf, n); */ - TRACE_TEXT("(dtlk_write_bytes"); - while (n-- > 0) - val = dtlk_write_tts(*buf++); - TRACE_RET; - return val; -} - -static char dtlk_write_tts(char ch) -{ - int retries = 0; -#ifdef TRACINGMORE - printk(" dtlk_write_tts("); - if (' ' <= ch && ch <= '~') - printk("'%c'", ch); - else - printk("0x%02x", ch); -#endif - if (ch != DTLK_CLEAR) /* no flow control for CLEAR command */ - while ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0 && - retries++ < DTLK_MAX_RETRIES) /* DT ready? */ - ; - if (retries > DTLK_MAX_RETRIES) - printk(KERN_ERR "dtlk_write_tts() timeout\n"); - - outb_p(ch, dtlk_port_tts); /* output to TTS port */ - /* the RDY bit goes zero 2-3 usec after writing, and goes - 1 again 180-190 usec later. Here, we wait up to 10 - usec for the RDY bit to go zero. */ - for (retries = 0; retries < loops_per_jiffy / (100000/HZ); retries++) - if ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0) - break; - -#ifdef TRACINGMORE - printk(")\n"); -#endif - return 0; -} - -MODULE_DESCRIPTION("RC Systems DoubleTalk PC speech card driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/char/hangcheck-timer.c b/drivers/char/hangcheck-timer.c index 4181bcc1c796..231cbf7b300f 100644 --- a/drivers/char/hangcheck-timer.c +++ b/drivers/char/hangcheck-timer.c @@ -69,7 +69,8 @@ MODULE_VERSION(VERSION_STR); static int __init hangcheck_parse_tick(char *str) { int par; - if (get_option(&str,&par)) + + if (get_option(&str, &par)) hangcheck_tick = par; return 1; } @@ -77,7 +78,8 @@ static int __init hangcheck_parse_tick(char *str) static int __init hangcheck_parse_margin(char *str) { int par; - if (get_option(&str,&par)) + + if (get_option(&str, &par)) hangcheck_margin = par; return 1; } @@ -85,7 +87,8 @@ static int __init hangcheck_parse_margin(char *str) static int __init hangcheck_parse_reboot(char *str) { int par; - if (get_option(&str,&par)) + + if (get_option(&str, &par)) hangcheck_reboot = par; return 1; } @@ -93,7 +96,8 @@ static int __init hangcheck_parse_reboot(char *str) static int __init hangcheck_parse_dump_tasks(char *str) { int par; - if (get_option(&str,&par)) + + if (get_option(&str, &par)) hangcheck_dump_tasks = par; return 1; } @@ -126,23 +130,23 @@ static void hangcheck_fire(struct timer_list *unused) if (tsc_diff > hangcheck_tsc_margin) { if (hangcheck_dump_tasks) { - printk(KERN_CRIT "Hangcheck: Task state:\n"); + pr_crit("Hangcheck: Task state:\n"); #ifdef CONFIG_MAGIC_SYSRQ handle_sysrq('t'); #endif /* CONFIG_MAGIC_SYSRQ */ } if (hangcheck_reboot) { - printk(KERN_CRIT "Hangcheck: hangcheck is restarting the machine.\n"); + pr_crit("Hangcheck: hangcheck is restarting the machine.\n"); emergency_restart(); } else { - printk(KERN_CRIT "Hangcheck: hangcheck value past margin!\n"); + pr_crit("Hangcheck: hangcheck value past margin!\n"); } } #if 0 /* * Enable to investigate delays in detail */ - printk("Hangcheck: called %Ld ns since last time (%Ld ns overshoot)\n", + pr_debug("Hangcheck: called %lld ns since last time (%lld ns overshoot)\n", tsc_diff, tsc_diff - hangcheck_tick*TIMER_FREQ); #endif mod_timer(&hangcheck_ticktock, jiffies + (hangcheck_tick*HZ)); @@ -152,7 +156,7 @@ static void hangcheck_fire(struct timer_list *unused) static int __init hangcheck_init(void) { - printk("Hangcheck: starting hangcheck timer %s (tick is %d seconds, margin is %d seconds).\n", + pr_debug("Hangcheck: starting hangcheck timer %s (tick is %d seconds, margin is %d seconds).\n", VERSION_STR, hangcheck_tick, hangcheck_margin); hangcheck_tsc_margin = (unsigned long long)hangcheck_margin + hangcheck_tick; @@ -167,8 +171,8 @@ static int __init hangcheck_init(void) static void __exit hangcheck_exit(void) { - del_timer_sync(&hangcheck_ticktock); - printk("Hangcheck: Stopped hangcheck timer.\n"); + timer_delete_sync(&hangcheck_ticktock); + pr_debug("Hangcheck: Stopped hangcheck timer.\n"); } module_init(hangcheck_init); diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index e110857824fc..285c6037417a 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -34,6 +34,7 @@ #include <linux/io.h> #include <linux/acpi.h> #include <linux/hpet.h> +#include <linux/platform_device.h> #include <asm/current.h> #include <asm/irq.h> #include <asm/div64.h> @@ -354,8 +355,9 @@ static __init int hpet_mmap_enable(char *str) } __setup("hpet_mmap=", hpet_mmap_enable); -static int hpet_mmap(struct file *file, struct vm_area_struct *vma) +static int hpet_mmap_prepare(struct vm_area_desc *desc) { + struct file *file = desc->file; struct hpet_dev *devp; unsigned long addr; @@ -368,11 +370,12 @@ static int hpet_mmap(struct file *file, struct vm_area_struct *vma) if (addr & (PAGE_SIZE - 1)) return -ENOSYS; - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - return vm_iomap_memory(vma, addr, PAGE_SIZE); + desc->page_prot = pgprot_noncached(desc->page_prot); + mmap_action_simple_ioremap(desc, addr, PAGE_SIZE); + return 0; } #else -static int hpet_mmap(struct file *file, struct vm_area_struct *vma) +static int hpet_mmap_prepare(struct vm_area_desc *desc) { return -ENOSYS; } @@ -710,7 +713,7 @@ static const struct file_operations hpet_fops = { .open = hpet_open, .release = hpet_release, .fasync = hpet_fasync, - .mmap = hpet_mmap, + .mmap_prepare = hpet_mmap_prepare, }; static int hpet_is_known(struct hpet_data *hdp) @@ -823,8 +826,7 @@ int hpet_alloc(struct hpet_data *hdp) return 0; } - hpetp = kzalloc(struct_size(hpetp, hp_dev, hdp->hd_nirqs), - GFP_KERNEL); + hpetp = kzalloc_flex(*hpetp, hp_dev, hdp->hd_nirqs); if (!hpetp) return -ENOMEM; @@ -867,7 +869,7 @@ int hpet_alloc(struct hpet_data *hdp) printk(KERN_INFO "hpet%u: at MMIO 0x%lx, IRQ%s", hpetp->hp_which, hdp->hd_phys_address, - hpetp->hp_ntimer > 1 ? "s" : ""); + str_plural(hpetp->hp_ntimer)); for (i = 0; i < hpetp->hp_ntimer; i++) printk(KERN_CONT "%s %u", i > 0 ? "," : "", hdp->hd_irq[i]); printk(KERN_CONT "\n"); @@ -972,11 +974,16 @@ static acpi_status hpet_resources(struct acpi_resource *res, void *data) return AE_OK; } -static int hpet_acpi_add(struct acpi_device *device) +static int hpet_acpi_probe(struct platform_device *pdev) { + struct acpi_device *device; acpi_status result; struct hpet_data data; + device = ACPI_COMPANION(&pdev->dev); + if (!device) + return -ENODEV; + memset(&data, 0, sizeof(data)); result = @@ -1001,12 +1008,12 @@ static const struct acpi_device_id hpet_device_ids[] = { {"", 0}, }; -static struct acpi_driver hpet_acpi_driver = { - .name = "hpet", - .ids = hpet_device_ids, - .ops = { - .add = hpet_acpi_add, - }, +static struct platform_driver hpet_acpi_driver = { + .probe = hpet_acpi_probe, + .driver = { + .name = "hpet_acpi", + .acpi_match_table = hpet_device_ids, + }, }; static struct miscdevice hpet_misc = { HPET_MINOR, "hpet", &hpet_fops }; @@ -1021,10 +1028,9 @@ static int __init hpet_init(void) sysctl_header = register_sysctl("dev/hpet", hpet_table); - result = acpi_bus_register_driver(&hpet_acpi_driver); + result = platform_driver_register(&hpet_acpi_driver); if (result < 0) { - if (sysctl_header) - unregister_sysctl_table(sysctl_header); + unregister_sysctl_table(sysctl_header); misc_deregister(&hpet_misc); return result; } diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index 17854f052386..a5bcef4a54ee 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -77,7 +77,7 @@ config HW_RANDOM_AIROHA config HW_RANDOM_ATMEL tristate "Atmel Random Number Generator support" - depends on (ARCH_AT91 || COMPILE_TEST) + depends on (ARCH_MICROCHIP || COMPILE_TEST) default HW_RANDOM help This driver provides kernel-side support for the Random Number @@ -312,6 +312,7 @@ config HW_RANDOM_INGENIC_TRNG config HW_RANDOM_NOMADIK tristate "ST-Ericsson Nomadik Random Number Generator support" depends on ARCH_NOMADIK || COMPILE_TEST + depends on ARM_AMBA default HW_RANDOM help This driver provides kernel-side support for the Random Number @@ -372,6 +373,16 @@ config HW_RANDOM_HISTB To compile this driver as a module, choose M here: the module will be called histb-rng. +config HW_RANDOM_HISI_TRNG + tristate "HiSilicon True Random Number Generator support" + depends on ARM64 && ACPI + help + This driver provides kernel-side support for the True Random Number + Generator hardware found on some HiSilicon SoCs. + + To compile this driver as a module, choose M here: the module will be + called hisi-trng-v2. + config HW_RANDOM_ST tristate "ST Microelectronics HW Random Number Generator support" depends on ARCH_STI || COMPILE_TEST @@ -534,10 +545,10 @@ config HW_RANDOM_NPCM If unsure, say Y. config HW_RANDOM_KEYSTONE + tristate "TI Keystone NETCP SA Hardware random number generator" depends on ARCH_KEYSTONE || COMPILE_TEST depends on HAS_IOMEM && OF default HW_RANDOM - tristate "TI Keystone NETCP SA Hardware random number generator" help This option enables Keystone's hardware random generator. @@ -579,15 +590,15 @@ config HW_RANDOM_ARM_SMCCC_TRNG module will be called arm_smccc_trng. config HW_RANDOM_CN10K - tristate "Marvell CN10K Random Number Generator support" - depends on HW_RANDOM && PCI && (ARM64 || (64BIT && COMPILE_TEST)) - default HW_RANDOM if ARCH_THUNDER - help - This driver provides support for the True Random Number - generator available in Marvell CN10K SoCs. + tristate "Marvell CN10K Random Number Generator support" + depends on HW_RANDOM && PCI && (ARM64 || (64BIT && COMPILE_TEST)) + default HW_RANDOM if ARCH_THUNDER + help + This driver provides support for the True Random Number + generator available in Marvell CN10K SoCs. - To compile this driver as a module, choose M here. - The module will be called cn10k_rng. If unsure, say Y. + To compile this driver as a module, choose M here. + The module will be called cn10k_rng. If unsure, say Y. config HW_RANDOM_JH7110 tristate "StarFive JH7110 Random Number Generator support" @@ -606,13 +617,25 @@ config HW_RANDOM_ROCKCHIP default HW_RANDOM help This driver provides kernel-side support for the True Random Number - Generator hardware found on some Rockchip SoC like RK3566 or RK3568. + Generator hardware found on some Rockchip SoCs like RK3566, RK3568 + or RK3588. To compile this driver as a module, choose M here: the module will be called rockchip-rng. If unsure, say Y. +config HW_RANDOM_XILINX + tristate "Support for Xilinx True Random Generator" + depends on ZYNQMP_FIRMWARE || COMPILE_TEST + select CRYPTO_LIB_SHA512 + help + Xilinx Versal SoC driver provides kernel-side support for True Random Number + Generator and Pseudo random Number in CTR_DRBG mode as defined in NIST SP800-90A. + + To compile this driver as a module, choose M here: the module + will be called xilinx-trng. + endif # HW_RANDOM config UML_RANDOM diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index b9132b3f5d21..95b5adb49560 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o obj-$(CONFIG_HW_RANDOM_POWERNV) += powernv-rng.o obj-$(CONFIG_HW_RANDOM_HISI) += hisi-rng.o obj-$(CONFIG_HW_RANDOM_HISTB) += histb-rng.o +obj-$(CONFIG_HW_RANDOM_HISI_TRNG) += hisi-trng-v2.o obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o obj-$(CONFIG_HW_RANDOM_BCM74110) += bcm74110-rng.o obj-$(CONFIG_HW_RANDOM_IPROC_RNG200) += iproc-rng200.o @@ -52,3 +53,4 @@ obj-$(CONFIG_HW_RANDOM_CN10K) += cn10k-rng.o obj-$(CONFIG_HW_RANDOM_POLARFIRE_SOC) += mpfs-rng.o obj-$(CONFIG_HW_RANDOM_ROCKCHIP) += rockchip-rng.o obj-$(CONFIG_HW_RANDOM_JH7110) += jh7110-trng.o +obj-$(CONFIG_HW_RANDOM_XILINX) += xilinx-trng.o diff --git a/drivers/char/hw_random/airoha-trng.c b/drivers/char/hw_random/airoha-trng.c index 1dbfa9505c21..076519a2f100 100644 --- a/drivers/char/hw_random/airoha-trng.c +++ b/drivers/char/hw_random/airoha-trng.c @@ -3,7 +3,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/bitfield.h> #include <linux/delay.h> #include <linux/hw_random.h> @@ -212,6 +211,7 @@ static int airoha_trng_probe(struct platform_device *pdev) trng->rng.init = airoha_trng_init; trng->rng.cleanup = airoha_trng_cleanup; trng->rng.read = airoha_trng_read; + trng->rng.quality = 900; ret = devm_hwrng_register(dev, &trng->rng); if (ret) { diff --git a/drivers/char/hw_random/amd-rng.c b/drivers/char/hw_random/amd-rng.c index 9a24d19236dc..dff80daae587 100644 --- a/drivers/char/hw_random/amd-rng.c +++ b/drivers/char/hw_random/amd-rng.c @@ -47,9 +47,9 @@ * want to register another driver on the same PCI id. */ static const struct pci_device_id pci_tbl[] = { - { PCI_VDEVICE(AMD, 0x7443), 0, }, - { PCI_VDEVICE(AMD, 0x746b), 0, }, - { 0, }, /* terminate list */ + { PCI_VDEVICE(AMD, 0x7443) }, + { PCI_VDEVICE(AMD, 0x746b) }, + { } /* terminate list */ }; MODULE_DEVICE_TABLE(pci, pci_tbl); @@ -154,7 +154,7 @@ found: goto put_dev; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = kzalloc_obj(*priv); if (!priv) { err = -ENOMEM; goto put_dev; diff --git a/drivers/char/hw_random/atmel-rng.c b/drivers/char/hw_random/atmel-rng.c index 143406bc6939..4ebbc44fecf0 100644 --- a/drivers/char/hw_random/atmel-rng.c +++ b/drivers/char/hw_random/atmel-rng.c @@ -8,7 +8,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/slab.h> #include <linux/err.h> #include <linux/clk.h> @@ -37,6 +36,7 @@ struct atmel_trng { struct clk *clk; void __iomem *base; struct hwrng rng; + struct device *dev; bool has_half_rate; }; @@ -59,9 +59,9 @@ static int atmel_trng_read(struct hwrng *rng, void *buf, size_t max, u32 *data = buf; int ret; - ret = pm_runtime_get_sync((struct device *)trng->rng.priv); + ret = pm_runtime_get_sync(trng->dev); if (ret < 0) { - pm_runtime_put_sync((struct device *)trng->rng.priv); + pm_runtime_put_sync(trng->dev); return ret; } @@ -79,8 +79,7 @@ static int atmel_trng_read(struct hwrng *rng, void *buf, size_t max, ret = 4; out: - pm_runtime_mark_last_busy((struct device *)trng->rng.priv); - pm_runtime_put_sync_autosuspend((struct device *)trng->rng.priv); + pm_runtime_put_sync_autosuspend(trng->dev); return ret; } @@ -134,9 +133,9 @@ static int atmel_trng_probe(struct platform_device *pdev) return -ENODEV; trng->has_half_rate = data->has_half_rate; + trng->dev = &pdev->dev; trng->rng.name = pdev->name; trng->rng.read = atmel_trng_read; - trng->rng.priv = (unsigned long)&pdev->dev; platform_set_drvdata(pdev, trng); #ifndef CONFIG_PM diff --git a/drivers/char/hw_random/ba431-rng.c b/drivers/char/hw_random/ba431-rng.c index 9de7466e6896..b0a39032c610 100644 --- a/drivers/char/hw_random/ba431-rng.c +++ b/drivers/char/hw_random/ba431-rng.c @@ -6,7 +6,6 @@ #include <linux/io.h> #include <linux/iopoll.h> #include <linux/kernel.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/workqueue.h> diff --git a/drivers/char/hw_random/bcm2835-rng.c b/drivers/char/hw_random/bcm2835-rng.c index aa2b135e3ee2..6d6ac409efcf 100644 --- a/drivers/char/hw_random/bcm2835-rng.c +++ b/drivers/char/hw_random/bcm2835-rng.c @@ -138,12 +138,11 @@ static const struct of_device_id bcm2835_rng_of_match[] = { { .compatible = "brcm,bcm6368-rng"}, {}, }; +MODULE_DEVICE_TABLE(of, bcm2835_rng_of_match); static int bcm2835_rng_probe(struct platform_device *pdev) { - const struct bcm2835_rng_of_data *of_data; struct device *dev = &pdev->dev; - const struct of_device_id *rng_id; struct bcm2835_rng_priv *priv; int err; @@ -171,12 +170,10 @@ static int bcm2835_rng_probe(struct platform_device *pdev) priv->rng.cleanup = bcm2835_rng_cleanup; if (dev_of_node(dev)) { - rng_id = of_match_node(bcm2835_rng_of_match, dev->of_node); - if (!rng_id) - return -EINVAL; + const struct bcm2835_rng_of_data *of_data; /* Check for rng init function, execute it */ - of_data = rng_id->data; + of_data = of_device_get_match_data(dev); if (of_data) priv->mask_interrupts = of_data->mask_interrupts; } @@ -191,8 +188,6 @@ static int bcm2835_rng_probe(struct platform_device *pdev) return err; } -MODULE_DEVICE_TABLE(of, bcm2835_rng_of_match); - static const struct platform_device_id bcm2835_rng_devtype[] = { { .name = "bcm2835-rng" }, { .name = "bcm63xx-rng" }, diff --git a/drivers/char/hw_random/bcm74110-rng.c b/drivers/char/hw_random/bcm74110-rng.c index 5c64148e91f1..4ff9ac45202a 100644 --- a/drivers/char/hw_random/bcm74110-rng.c +++ b/drivers/char/hw_random/bcm74110-rng.c @@ -6,7 +6,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/kernel.h> #include <linux/io.h> #include <linux/delay.h> diff --git a/drivers/char/hw_random/cavium-rng.c b/drivers/char/hw_random/cavium-rng.c index d9d7b6038c06..3e2c042009f6 100644 --- a/drivers/char/hw_random/cavium-rng.c +++ b/drivers/char/hw_random/cavium-rng.c @@ -73,8 +73,8 @@ static void cavium_rng_remove(struct pci_dev *pdev) } static const struct pci_device_id cavium_rng_pf_id_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xa018), 0, 0, 0}, /* Thunder RNM */ - {0,}, + { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xa018) }, /* Thunder RNM */ + { }, }; MODULE_DEVICE_TABLE(pci, cavium_rng_pf_id_table); diff --git a/drivers/char/hw_random/cctrng.c b/drivers/char/hw_random/cctrng.c index 4db198849695..a5be9258037f 100644 --- a/drivers/char/hw_random/cctrng.c +++ b/drivers/char/hw_random/cctrng.c @@ -98,7 +98,6 @@ static void cc_trng_pm_put_suspend(struct device *dev) { int rc = 0; - pm_runtime_mark_last_busy(dev); rc = pm_runtime_put_autosuspend(dev); if (rc) dev_err(dev, "pm_runtime_put_autosuspend returned %x\n", rc); diff --git a/drivers/char/hw_random/cn10k-rng.c b/drivers/char/hw_random/cn10k-rng.c index 31935316a160..3b4e78182e14 100644 --- a/drivers/char/hw_random/cn10k-rng.c +++ b/drivers/char/hw_random/cn10k-rng.c @@ -188,7 +188,7 @@ static int cn10k_rng_probe(struct pci_dev *pdev, const struct pci_device_id *id) rng->reg_base = pcim_iomap(pdev, 0, 0); if (!rng->reg_base) - return dev_err_probe(&pdev->dev, -ENOMEM, "Error while mapping CSRs, exiting\n"); + return -ENOMEM; rng->ops.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "cn10k-rng-%s", dev_name(&pdev->dev)); diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 018316f54621..6931657ad2ca 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -20,23 +20,26 @@ #include <linux/miscdevice.h> #include <linux/module.h> #include <linux/random.h> +#include <linux/rcupdate.h> #include <linux/sched.h> #include <linux/sched/signal.h> #include <linux/slab.h> #include <linux/string.h> +#include <linux/sysfs.h> #include <linux/uaccess.h> +#include <linux/workqueue.h> #define RNG_MODULE_NAME "hw_random" -#define RNG_BUFFER_SIZE (SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES) +#define RNG_BUFFER_SIZE MAX(32, SMP_CACHE_BYTES) -static struct hwrng *current_rng; +static struct hwrng __rcu *current_rng; /* the current rng has been explicitly chosen by user via sysfs */ static int cur_rng_set_by_user; static struct task_struct *hwrng_fill; /* list of registered rngs */ static LIST_HEAD(rng_list); -/* Protects rng_list and current_rng */ +/* Protects rng_list, hwrng_fill and updating on current_rng */ static DEFINE_MUTEX(rng_mutex); /* Protects rng read functions, data_avail, rng_buffer and rng_fillbuf */ static DEFINE_MUTEX(reading_mutex); @@ -52,30 +55,47 @@ module_param(default_quality, ushort, 0644); MODULE_PARM_DESC(default_quality, "default maximum entropy content of hwrng per 1024 bits of input"); -static void drop_current_rng(void); static int hwrng_init(struct hwrng *rng); static int hwrng_fillfn(void *unused); -static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size, - int wait); - static size_t rng_buffer_size(void) { return RNG_BUFFER_SIZE; } -static inline void cleanup_rng(struct kref *kref) +static void cleanup_rng_work(struct work_struct *work) { - struct hwrng *rng = container_of(kref, struct hwrng, ref); + struct hwrng *rng = container_of(work, struct hwrng, cleanup_work); + + /* + * Hold rng_mutex here so we serialize in case they set_current_rng + * on rng again immediately. + */ + mutex_lock(&rng_mutex); + + /* Skip if rng has been reinitialized. */ + if (kref_read(&rng->ref)) { + mutex_unlock(&rng_mutex); + return; + } if (rng->cleanup) rng->cleanup(rng); complete(&rng->cleanup_done); + mutex_unlock(&rng_mutex); +} + +static inline void cleanup_rng(struct kref *kref) +{ + struct hwrng *rng = container_of(kref, struct hwrng, ref); + + schedule_work(&rng->cleanup_work); } static int set_current_rng(struct hwrng *rng) { + struct hwrng *old_rng; int err; BUG_ON(!mutex_is_locked(&rng_mutex)); @@ -84,8 +104,14 @@ static int set_current_rng(struct hwrng *rng) if (err) return err; - drop_current_rng(); - current_rng = rng; + old_rng = rcu_dereference_protected(current_rng, + lockdep_is_held(&rng_mutex)); + rcu_assign_pointer(current_rng, rng); + + if (old_rng) { + synchronize_rcu(); + kref_put(&old_rng->ref, cleanup_rng); + } /* if necessary, start hwrng thread */ if (!hwrng_fill) { @@ -101,47 +127,56 @@ static int set_current_rng(struct hwrng *rng) static void drop_current_rng(void) { - BUG_ON(!mutex_is_locked(&rng_mutex)); - if (!current_rng) + struct hwrng *rng; + + rng = rcu_dereference_protected(current_rng, + lockdep_is_held(&rng_mutex)); + if (!rng) return; + RCU_INIT_POINTER(current_rng, NULL); + synchronize_rcu(); + + if (hwrng_fill) { + kthread_stop(hwrng_fill); + hwrng_fill = NULL; + } + /* decrease last reference for triggering the cleanup */ - kref_put(¤t_rng->ref, cleanup_rng); - current_rng = NULL; + kref_put(&rng->ref, cleanup_rng); } -/* Returns ERR_PTR(), NULL or refcounted hwrng */ +/* Returns NULL or refcounted hwrng */ static struct hwrng *get_current_rng_nolock(void) { - if (current_rng) - kref_get(¤t_rng->ref); + struct hwrng *rng; + + rng = rcu_dereference_protected(current_rng, + lockdep_is_held(&rng_mutex)); + if (rng) + kref_get(&rng->ref); - return current_rng; + return rng; } static struct hwrng *get_current_rng(void) { struct hwrng *rng; - if (mutex_lock_interruptible(&rng_mutex)) - return ERR_PTR(-ERESTARTSYS); + rcu_read_lock(); + rng = rcu_dereference(current_rng); + if (rng) + kref_get(&rng->ref); - rng = get_current_rng_nolock(); + rcu_read_unlock(); - mutex_unlock(&rng_mutex); return rng; } static void put_rng(struct hwrng *rng) { - /* - * Hold rng_mutex here so we serialize in case they set_current_rng - * on rng again immediately. - */ - mutex_lock(&rng_mutex); if (rng) kref_put(&rng->ref, cleanup_rng); - mutex_unlock(&rng_mutex); } static int hwrng_init(struct hwrng *rng) @@ -176,8 +211,8 @@ static int rng_dev_open(struct inode *inode, struct file *filp) return 0; } -static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size, - int wait) { +static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size, bool wait) +{ int present; BUG_ON(!mutex_is_locked(&reading_mutex)); @@ -213,10 +248,6 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf, while (size) { rng = get_current_rng(); - if (IS_ERR(rng)) { - err = PTR_ERR(rng); - goto out; - } if (!rng) { err = -ENODEV; goto out; @@ -303,7 +334,7 @@ static struct miscdevice rng_miscdev = { static int enable_best_rng(void) { - struct hwrng *rng, *new_rng = NULL; + struct hwrng *rng, *cur_rng, *new_rng = NULL; int ret = -ENODEV; BUG_ON(!mutex_is_locked(&rng_mutex)); @@ -321,7 +352,9 @@ static int enable_best_rng(void) new_rng = rng; } - ret = ((new_rng == current_rng) ? 0 : set_current_rng(new_rng)); + cur_rng = rcu_dereference_protected(current_rng, + lockdep_is_held(&rng_mutex)); + ret = ((new_rng == cur_rng) ? 0 : set_current_rng(new_rng)); if (!ret) cur_rng_set_by_user = 0; @@ -341,6 +374,9 @@ static ssize_t rng_current_store(struct device *dev, if (sysfs_streq(buf, "")) { err = enable_best_rng(); + } else if (sysfs_streq(buf, "none")) { + cur_rng_set_by_user = 1; + drop_current_rng(); } else { list_for_each_entry(rng, &rng_list, list) { if (sysfs_streq(rng->name, buf)) { @@ -368,8 +404,6 @@ static ssize_t rng_current_show(struct device *dev, struct hwrng *rng; rng = get_current_rng(); - if (IS_ERR(rng)) - return PTR_ERR(rng); ret = sysfs_emit(buf, "%s\n", rng ? rng->name : "none"); put_rng(rng); @@ -381,21 +415,17 @@ static ssize_t rng_available_show(struct device *dev, struct device_attribute *attr, char *buf) { - int err; struct hwrng *rng; + int len = 0; - err = mutex_lock_interruptible(&rng_mutex); - if (err) + if (mutex_lock_interruptible(&rng_mutex)) return -ERESTARTSYS; - buf[0] = '\0'; - list_for_each_entry(rng, &rng_list, list) { - strlcat(buf, rng->name, PAGE_SIZE); - strlcat(buf, " ", PAGE_SIZE); - } - strlcat(buf, "\n", PAGE_SIZE); + list_for_each_entry(rng, &rng_list, list) + len += sysfs_emit_at(buf, len, "%s ", rng->name); + len += sysfs_emit_at(buf, len, "none\n"); mutex_unlock(&rng_mutex); - return strlen(buf); + return len; } static ssize_t rng_selected_show(struct device *dev, @@ -413,8 +443,6 @@ static ssize_t rng_quality_show(struct device *dev, struct hwrng *rng; rng = get_current_rng(); - if (IS_ERR(rng)) - return PTR_ERR(rng); if (!rng) /* no need to put_rng */ return -ENODEV; @@ -429,6 +457,7 @@ static ssize_t rng_quality_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { + struct hwrng *rng; u16 quality; int ret = -EINVAL; @@ -445,12 +474,13 @@ static ssize_t rng_quality_store(struct device *dev, goto out; } - if (!current_rng) { + rng = rcu_dereference_protected(current_rng, lockdep_is_held(&rng_mutex)); + if (!rng) { ret = -ENODEV; goto out; } - current_rng->quality = quality; + rng->quality = quality; current_quality = quality; /* obsolete */ /* the best available RNG may have changed */ @@ -486,11 +516,22 @@ static int hwrng_fillfn(void *unused) struct hwrng *rng; rng = get_current_rng(); - if (IS_ERR(rng) || !rng) + if (!rng) { + /* + * Keep the task_struct alive until kthread_stop() + * is called to avoid UAF in drop_current_rng(). + */ + while (!kthread_should_stop()) { + set_current_state(TASK_INTERRUPTIBLE); + if (!kthread_should_stop()) + schedule(); + } + set_current_state(TASK_RUNNING); break; + } + mutex_lock(&reading_mutex); - rc = rng_get_data(rng, rng_fillbuf, - rng_buffer_size(), 1); + rc = rng_get_data(rng, rng_fillbuf, rng_buffer_size(), true); if (current_quality != rng->quality) rng->quality = current_quality; /* obsolete */ quality = rng->quality; @@ -515,14 +556,13 @@ static int hwrng_fillfn(void *unused) add_hwgenerator_randomness((void *)rng_fillbuf, rc, entropy >> 10, true); } - hwrng_fill = NULL; return 0; } int hwrng_register(struct hwrng *rng) { int err = -EINVAL; - struct hwrng *tmp; + struct hwrng *cur_rng, *tmp; if (!rng->name || (!rng->data_read && !rng->read)) goto out; @@ -537,23 +577,27 @@ int hwrng_register(struct hwrng *rng) } list_add_tail(&rng->list, &rng_list); + INIT_WORK(&rng->cleanup_work, cleanup_rng_work); init_completion(&rng->cleanup_done); complete(&rng->cleanup_done); init_completion(&rng->dying); /* Adjust quality field to always have a proper value */ - rng->quality = min_t(u16, min_t(u16, default_quality, 1024), rng->quality ?: 1024); - - if (!current_rng || - (!cur_rng_set_by_user && rng->quality > current_rng->quality)) { - /* - * Set new rng as current as the new rng source - * provides better entropy quality and was not - * chosen by userspace. - */ - err = set_current_rng(rng); - if (err) - goto out_unlock; + rng->quality = min3(default_quality, 1024, rng->quality ?: 1024); + + if (!cur_rng_set_by_user) { + cur_rng = rcu_dereference_protected(current_rng, + lockdep_is_held(&rng_mutex)); + if (!cur_rng || rng->quality > cur_rng->quality) { + /* + * Set new rng as current as the new rng source + * provides better entropy quality and was not + * chosen by userspace. + */ + err = set_current_rng(rng); + if (err) + goto out_unlock; + } } mutex_unlock(&rng_mutex); return 0; @@ -566,14 +610,17 @@ EXPORT_SYMBOL_GPL(hwrng_register); void hwrng_unregister(struct hwrng *rng) { - struct hwrng *new_rng; + struct hwrng *cur_rng; int err; mutex_lock(&rng_mutex); list_del(&rng->list); complete_all(&rng->dying); - if (current_rng == rng) { + + cur_rng = rcu_dereference_protected(current_rng, + lockdep_is_held(&rng_mutex)); + if (cur_rng == rng) { err = enable_best_rng(); if (err) { drop_current_rng(); @@ -581,17 +628,7 @@ void hwrng_unregister(struct hwrng *rng) } } - new_rng = get_current_rng_nolock(); - if (list_empty(&rng_list)) { - mutex_unlock(&rng_mutex); - if (hwrng_fill) - kthread_stop(hwrng_fill); - } else - mutex_unlock(&rng_mutex); - - if (new_rng) - put_rng(new_rng); - + mutex_unlock(&rng_mutex); wait_for_completion(&rng->cleanup_done); } EXPORT_SYMBOL_GPL(hwrng_unregister); @@ -679,7 +716,7 @@ static int __init hwrng_modinit(void) static void __exit hwrng_modexit(void) { mutex_lock(&rng_mutex); - BUG_ON(current_rng); + WARN_ON(rcu_access_pointer(current_rng)); kfree(rng_buffer); kfree(rng_fillbuf); mutex_unlock(&rng_mutex); diff --git a/drivers/char/hw_random/exynos-trng.c b/drivers/char/hw_random/exynos-trng.c index 02e207c09e81..1fcc7eb121c2 100644 --- a/drivers/char/hw_random/exynos-trng.c +++ b/drivers/char/hw_random/exynos-trng.c @@ -20,7 +20,6 @@ #include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/property.h> diff --git a/drivers/char/hw_random/geode-rng.c b/drivers/char/hw_random/geode-rng.c index 159baf00a867..ae63eff64344 100644 --- a/drivers/char/hw_random/geode-rng.c +++ b/drivers/char/hw_random/geode-rng.c @@ -46,8 +46,8 @@ * want to register another driver on the same PCI id. */ static const struct pci_device_id pci_tbl[] = { - { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_LX_AES), 0, }, - { 0, }, /* terminate list */ + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_LX_AES) }, + { } /* terminate list */ }; MODULE_DEVICE_TABLE(pci, pci_tbl); @@ -107,7 +107,7 @@ static int __init geode_rng_init(void) return err; found: - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = kzalloc_obj(*priv); if (!priv) { err = -ENOMEM; goto put_dev; diff --git a/drivers/char/hw_random/hisi-trng-v2.c b/drivers/char/hw_random/hisi-trng-v2.c new file mode 100644 index 000000000000..6584ed051e09 --- /dev/null +++ b/drivers/char/hw_random/hisi-trng-v2.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2019 HiSilicon Limited. */ + +#include <linux/acpi.h> +#include <linux/err.h> +#include <linux/hw_random.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/random.h> + +#define HISI_TRNG_REG 0x00F0 +#define HISI_TRNG_BYTES 4 +#define HISI_TRNG_QUALITY 512 +#define SLEEP_US 10 +#define TIMEOUT_US 10000 + +struct hisi_trng { + void __iomem *base; + struct hwrng rng; +}; + +static int hisi_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait) +{ + struct hisi_trng *trng; + int currsize = 0; + u32 val = 0; + int ret; + + trng = container_of(rng, struct hisi_trng, rng); + + do { + ret = readl_poll_timeout(trng->base + HISI_TRNG_REG, val, + val, SLEEP_US, TIMEOUT_US); + if (ret) + return currsize; + + if (max - currsize >= HISI_TRNG_BYTES) { + memcpy(buf + currsize, &val, HISI_TRNG_BYTES); + currsize += HISI_TRNG_BYTES; + if (currsize == max) + return currsize; + continue; + } + + /* copy remaining bytes */ + memcpy(buf + currsize, &val, max - currsize); + currsize = max; + } while (currsize < max); + + return currsize; +} + +static int hisi_trng_probe(struct platform_device *pdev) +{ + struct hisi_trng *trng; + int ret; + + trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL); + if (!trng) + return -ENOMEM; + + trng->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(trng->base)) + return PTR_ERR(trng->base); + + trng->rng.name = pdev->name; + trng->rng.read = hisi_trng_read; + trng->rng.quality = HISI_TRNG_QUALITY; + + ret = devm_hwrng_register(&pdev->dev, &trng->rng); + if (ret) + dev_err(&pdev->dev, "failed to register hwrng: %d!\n", ret); + return ret; +} + +static const struct acpi_device_id hisi_trng_acpi_match[] = { + { "HISI02B3", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, hisi_trng_acpi_match); + +static struct platform_driver hisi_trng_driver = { + .probe = hisi_trng_probe, + .driver = { + .name = "hisi-trng-v2", + .acpi_match_table = ACPI_PTR(hisi_trng_acpi_match), + }, +}; + +module_platform_driver(hisi_trng_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Weili Qian <qianweili@huawei.com>"); +MODULE_AUTHOR("Zaibo Xu <xuzaibo@huawei.com>"); +MODULE_DESCRIPTION("HiSilicon true random number generator V2 driver"); diff --git a/drivers/char/hw_random/histb-rng.c b/drivers/char/hw_random/histb-rng.c index 1b91e88cc4c0..445b80beed62 100644 --- a/drivers/char/hw_random/histb-rng.c +++ b/drivers/char/hw_random/histb-rng.c @@ -8,7 +8,6 @@ #include <linux/io.h> #include <linux/iopoll.h> #include <linux/kernel.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> diff --git a/drivers/char/hw_random/imx-rngc.c b/drivers/char/hw_random/imx-rngc.c index 118a72acb99b..28c56c2d1bf6 100644 --- a/drivers/char/hw_random/imx-rngc.c +++ b/drivers/char/hw_random/imx-rngc.c @@ -7,12 +7,13 @@ */ #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/clk.h> #include <linux/err.h> #include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> #include <linux/interrupt.h> #include <linux/hw_random.h> #include <linux/completion.h> @@ -53,6 +54,7 @@ #define RNGC_SELFTEST_TIMEOUT 2500 /* us */ #define RNGC_SEED_TIMEOUT 200 /* ms */ +#define RNGC_PM_TIMEOUT 500 /* ms */ static bool self_test = true; module_param(self_test, bool, 0); @@ -123,7 +125,11 @@ static int imx_rngc_read(struct hwrng *rng, void *data, size_t max, bool wait) { struct imx_rngc *rngc = container_of(rng, struct imx_rngc, rng); unsigned int status; - int retval = 0; + int err, retval = 0; + + err = pm_runtime_resume_and_get(rngc->dev); + if (err) + return err; while (max >= sizeof(u32)) { status = readl(rngc->base + RNGC_STATUS); @@ -141,6 +147,8 @@ static int imx_rngc_read(struct hwrng *rng, void *data, size_t max, bool wait) max -= sizeof(u32); } } + pm_runtime_mark_last_busy(rngc->dev); + pm_runtime_put(rngc->dev); return retval ? retval : -EIO; } @@ -169,7 +177,11 @@ static int imx_rngc_init(struct hwrng *rng) { struct imx_rngc *rngc = container_of(rng, struct imx_rngc, rng); u32 cmd, ctrl; - int ret; + int ret, err; + + err = pm_runtime_resume_and_get(rngc->dev); + if (err) + return err; /* clear error */ cmd = readl(rngc->base + RNGC_COMMAND); @@ -186,15 +198,15 @@ static int imx_rngc_init(struct hwrng *rng) ret = wait_for_completion_timeout(&rngc->rng_op_done, msecs_to_jiffies(RNGC_SEED_TIMEOUT)); if (!ret) { - ret = -ETIMEDOUT; - goto err; + err = -ETIMEDOUT; + goto out; } } while (rngc->err_reg == RNGC_ERROR_STATUS_STAT_ERR); if (rngc->err_reg) { - ret = -EIO; - goto err; + err = -EIO; + goto out; } /* @@ -205,23 +217,29 @@ static int imx_rngc_init(struct hwrng *rng) ctrl |= RNGC_CTRL_AUTO_SEED; writel(ctrl, rngc->base + RNGC_CONTROL); +out: /* * if initialisation was successful, we keep the interrupt * unmasked until imx_rngc_cleanup is called * we mask the interrupt ourselves if we return an error */ - return 0; + if (err) + imx_rngc_irq_mask_clear(rngc); -err: - imx_rngc_irq_mask_clear(rngc); - return ret; + pm_runtime_put(rngc->dev); + return err; } static void imx_rngc_cleanup(struct hwrng *rng) { struct imx_rngc *rngc = container_of(rng, struct imx_rngc, rng); + int err; - imx_rngc_irq_mask_clear(rngc); + err = pm_runtime_resume_and_get(rngc->dev); + if (!err) { + imx_rngc_irq_mask_clear(rngc); + pm_runtime_put(rngc->dev); + } } static int __init imx_rngc_probe(struct platform_device *pdev) @@ -240,7 +258,7 @@ static int __init imx_rngc_probe(struct platform_device *pdev) if (IS_ERR(rngc->base)) return PTR_ERR(rngc->base); - rngc->clk = devm_clk_get_enabled(&pdev->dev, NULL); + rngc->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(rngc->clk)) return dev_err_probe(&pdev->dev, PTR_ERR(rngc->clk), "Cannot get rng_clk\n"); @@ -248,14 +266,18 @@ static int __init imx_rngc_probe(struct platform_device *pdev) if (irq < 0) return irq; + clk_prepare_enable(rngc->clk); + ver_id = readl(rngc->base + RNGC_VER_ID); rng_type = FIELD_GET(RNG_TYPE, ver_id); /* * This driver supports only RNGC and RNGB. (There's a different * driver for RNGA.) */ - if (rng_type != RNGC_TYPE_RNGC && rng_type != RNGC_TYPE_RNGB) + if (rng_type != RNGC_TYPE_RNGC && rng_type != RNGC_TYPE_RNGB) { + clk_disable_unprepare(rngc->clk); return -ENODEV; + } init_completion(&rngc->rng_op_done); @@ -272,15 +294,24 @@ static int __init imx_rngc_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, irq, imx_rngc_irq, 0, pdev->name, (void *)rngc); - if (ret) + if (ret) { + clk_disable_unprepare(rngc->clk); return dev_err_probe(&pdev->dev, ret, "Can't get interrupt working.\n"); + } if (self_test) { ret = imx_rngc_self_test(rngc); - if (ret) + if (ret) { + clk_disable_unprepare(rngc->clk); return dev_err_probe(&pdev->dev, ret, "self test failed\n"); + } } + pm_runtime_set_autosuspend_delay(&pdev->dev, RNGC_PM_TIMEOUT); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + devm_pm_runtime_enable(&pdev->dev); + ret = devm_hwrng_register(&pdev->dev, &rngc->rng); if (ret) return dev_err_probe(&pdev->dev, ret, "hwrng registration failed\n"); @@ -310,7 +341,10 @@ static int imx_rngc_resume(struct device *dev) return 0; } -static DEFINE_SIMPLE_DEV_PM_OPS(imx_rngc_pm_ops, imx_rngc_suspend, imx_rngc_resume); +static const struct dev_pm_ops imx_rngc_pm_ops = { + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + RUNTIME_PM_OPS(imx_rngc_suspend, imx_rngc_resume, NULL) +}; static const struct of_device_id imx_rngc_dt_ids[] = { { .compatible = "fsl,imx25-rngb" }, @@ -321,7 +355,7 @@ MODULE_DEVICE_TABLE(of, imx_rngc_dt_ids); static struct platform_driver imx_rngc_driver = { .driver = { .name = KBUILD_MODNAME, - .pm = pm_sleep_ptr(&imx_rngc_pm_ops), + .pm = pm_ptr(&imx_rngc_pm_ops), .of_match_table = imx_rngc_dt_ids, }, }; diff --git a/drivers/char/hw_random/ingenic-trng.c b/drivers/char/hw_random/ingenic-trng.c index 1672320e7d3d..0dbe116346fd 100644 --- a/drivers/char/hw_random/ingenic-trng.c +++ b/drivers/char/hw_random/ingenic-trng.c @@ -11,7 +11,6 @@ #include <linux/hw_random.h> #include <linux/io.h> #include <linux/iopoll.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/slab.h> diff --git a/drivers/char/hw_random/intel-rng.c b/drivers/char/hw_random/intel-rng.c index 7b171cb3b825..d24e747203c1 100644 --- a/drivers/char/hw_random/intel-rng.c +++ b/drivers/char/hw_random/intel-rng.c @@ -346,7 +346,7 @@ static int __init intel_rng_mod_init(void) goto fwh_done; } - intel_rng_hw = kmalloc(sizeof(*intel_rng_hw), GFP_KERNEL); + intel_rng_hw = kmalloc_obj(*intel_rng_hw); if (!intel_rng_hw) { pci_dev_put(dev); goto out; diff --git a/drivers/char/hw_random/iproc-rng200.c b/drivers/char/hw_random/iproc-rng200.c index 440fe28bddc0..2e2aafca5cf0 100644 --- a/drivers/char/hw_random/iproc-rng200.c +++ b/drivers/char/hw_random/iproc-rng200.c @@ -12,7 +12,6 @@ #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/delay.h> diff --git a/drivers/char/hw_random/jh7110-trng.c b/drivers/char/hw_random/jh7110-trng.c index 9776f4daa044..4712c3c530e4 100644 --- a/drivers/char/hw_random/jh7110-trng.c +++ b/drivers/char/hw_random/jh7110-trng.c @@ -256,19 +256,22 @@ static int starfive_trng_read(struct hwrng *rng, void *buf, size_t max, bool wai if (wait) { ret = starfive_trng_wait_idle(trng); - if (ret) - return -ETIMEDOUT; + if (ret) { + ret = -ETIMEDOUT; + goto out_put; + } } ret = starfive_trng_cmd(trng, STARFIVE_CTRL_GENE_RANDNUM, wait); if (ret) - return ret; + goto out_put; memcpy_fromio(buf, trng->base + STARFIVE_RAND0, max); + ret = max; +out_put: pm_runtime_put_sync_autosuspend(trng->dev); - - return max; + return ret; } static int starfive_trng_probe(struct platform_device *pdev) diff --git a/drivers/char/hw_random/ks-sa-rng.c b/drivers/char/hw_random/ks-sa-rng.c index d8fd8a354482..9e408144a10c 100644 --- a/drivers/char/hw_random/ks-sa-rng.c +++ b/drivers/char/hw_random/ks-sa-rng.c @@ -231,6 +231,10 @@ static int ks_sa_rng_probe(struct platform_device *pdev) if (IS_ERR(ks_sa_rng->regmap_cfg)) return dev_err_probe(dev, -EINVAL, "syscon_node_to_regmap failed\n"); + ks_sa_rng->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(ks_sa_rng->clk)) + return dev_err_probe(dev, PTR_ERR(ks_sa_rng->clk), "Failed to get clock\n"); + pm_runtime_enable(dev); ret = pm_runtime_resume_and_get(dev); if (ret < 0) { diff --git a/drivers/char/hw_random/mtk-rng.c b/drivers/char/hw_random/mtk-rng.c index 1e3048f2bb38..1f9793010e6c 100644 --- a/drivers/char/hw_random/mtk-rng.c +++ b/drivers/char/hw_random/mtk-rng.c @@ -3,9 +3,11 @@ * Driver for Mediatek Hardware Random Number Generator * * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com> + * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org> */ #define MTK_RNG_DEV KBUILD_MODNAME +#include <linux/arm-smccc.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/err.h> @@ -17,6 +19,7 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/soc/mediatek/mtk_sip_svc.h> /* Runtime PM autosuspend timeout: */ #define RNG_AUTOSUSPEND_TIMEOUT 100 @@ -30,12 +33,19 @@ #define RNG_DATA 0x08 +/* Driver feature flags */ +#define MTK_RNG_SMC BIT(0) + +#define MTK_SIP_KERNEL_GET_RND MTK_SIP_SMC_CMD(0x550) + #define to_mtk_rng(p) container_of(p, struct mtk_rng, rng) struct mtk_rng { void __iomem *base; struct clk *clk; struct hwrng rng; + struct device *dev; + unsigned long flags; }; static int mtk_rng_init(struct hwrng *rng) @@ -85,7 +95,7 @@ static int mtk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) struct mtk_rng *priv = to_mtk_rng(rng); int retval = 0; - pm_runtime_get_sync((struct device *)priv->rng.priv); + pm_runtime_get_sync(priv->dev); while (max >= sizeof(u32)) { if (!mtk_rng_wait_ready(rng, wait)) @@ -97,12 +107,61 @@ static int mtk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) max -= sizeof(u32); } - pm_runtime_mark_last_busy((struct device *)priv->rng.priv); - pm_runtime_put_sync_autosuspend((struct device *)priv->rng.priv); + pm_runtime_put_sync_autosuspend(priv->dev); + + return retval || !wait ? retval : -EIO; +} + +static int mtk_rng_read_smc(struct hwrng *rng, void *buf, size_t max, + bool wait) +{ + struct arm_smccc_res res; + int retval = 0; + + while (max >= sizeof(u32)) { + arm_smccc_smc(MTK_SIP_KERNEL_GET_RND, 0, 0, 0, 0, 0, 0, 0, + &res); + if (res.a0) + break; + + *(u32 *)buf = res.a1; + retval += sizeof(u32); + buf += sizeof(u32); + max -= sizeof(u32); + } return retval || !wait ? retval : -EIO; } +static bool mtk_rng_hw_accessible(struct mtk_rng *priv) +{ + u32 val; + int err; + + err = clk_prepare_enable(priv->clk); + if (err) + return false; + + val = readl(priv->base + RNG_CTRL); + val |= RNG_EN; + writel(val, priv->base + RNG_CTRL); + + val = readl(priv->base + RNG_CTRL); + + if (val & RNG_EN) { + /* HW is accessible, clean up: disable RNG and clock */ + writel(val & ~RNG_EN, priv->base + RNG_CTRL); + clk_disable_unprepare(priv->clk); + return true; + } + + /* + * If TF-A blocks direct access, the register reads back as 0. + * Leave the clock enabled as TF-A needs it. + */ + return false; +} + static int mtk_rng_probe(struct platform_device *pdev) { int ret; @@ -112,25 +171,44 @@ static int mtk_rng_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; + priv->dev = &pdev->dev; priv->rng.name = pdev->name; -#ifndef CONFIG_PM - priv->rng.init = mtk_rng_init; - priv->rng.cleanup = mtk_rng_cleanup; -#endif - priv->rng.read = mtk_rng_read; - priv->rng.priv = (unsigned long)&pdev->dev; priv->rng.quality = 900; - - priv->clk = devm_clk_get(&pdev->dev, "rng"); - if (IS_ERR(priv->clk)) { - ret = PTR_ERR(priv->clk); - dev_err(&pdev->dev, "no clock for device: %d\n", ret); - return ret; + priv->flags = (unsigned long)device_get_match_data(&pdev->dev); + + if (!(priv->flags & MTK_RNG_SMC)) { + priv->clk = devm_clk_get(&pdev->dev, "rng"); + if (IS_ERR(priv->clk)) { + ret = PTR_ERR(priv->clk); + dev_err(&pdev->dev, "no clock for device: %d\n", ret); + return ret; + } + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + if (IS_ENABLED(CONFIG_HAVE_ARM_SMCCC) && + of_device_is_compatible(pdev->dev.of_node, + "mediatek,mt7986-rng") && + !mtk_rng_hw_accessible(priv)) { + priv->flags |= MTK_RNG_SMC; + dev_info(&pdev->dev, + "HW RNG not MMIO accessible, using SMC\n"); + } } - priv->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(priv->base)) - return PTR_ERR(priv->base); + if (priv->flags & MTK_RNG_SMC) { + if (!IS_ENABLED(CONFIG_HAVE_ARM_SMCCC)) + return -ENODEV; + priv->rng.read = mtk_rng_read_smc; + } else { +#ifndef CONFIG_PM + priv->rng.init = mtk_rng_init; + priv->rng.cleanup = mtk_rng_cleanup; +#endif + priv->rng.read = mtk_rng_read; + } ret = devm_hwrng_register(&pdev->dev, &priv->rng); if (ret) { @@ -139,10 +217,15 @@ static int mtk_rng_probe(struct platform_device *pdev) return ret; } - dev_set_drvdata(&pdev->dev, priv); - pm_runtime_set_autosuspend_delay(&pdev->dev, RNG_AUTOSUSPEND_TIMEOUT); - pm_runtime_use_autosuspend(&pdev->dev); - devm_pm_runtime_enable(&pdev->dev); + if (!(priv->flags & MTK_RNG_SMC)) { + dev_set_drvdata(&pdev->dev, priv); + pm_runtime_set_autosuspend_delay(&pdev->dev, + RNG_AUTOSUSPEND_TIMEOUT); + pm_runtime_use_autosuspend(&pdev->dev); + ret = devm_pm_runtime_enable(&pdev->dev); + if (ret) + return ret; + } dev_info(&pdev->dev, "registered RNG driver\n"); @@ -179,8 +262,11 @@ static const struct dev_pm_ops mtk_rng_pm_ops = { #endif /* CONFIG_PM */ static const struct of_device_id mtk_rng_match[] = { - { .compatible = "mediatek,mt7986-rng" }, { .compatible = "mediatek,mt7623-rng" }, + { .compatible = "mediatek,mt7981-rng", .data = (void *)MTK_RNG_SMC }, + { .compatible = "mediatek,mt7986-rng" }, + { .compatible = "mediatek,mt7987-rng", .data = (void *)MTK_RNG_SMC }, + { .compatible = "mediatek,mt7988-rng", .data = (void *)MTK_RNG_SMC }, {}, }; MODULE_DEVICE_TABLE(of, mtk_rng_match); @@ -198,4 +284,5 @@ module_platform_driver(mtk_rng_driver); MODULE_DESCRIPTION("Mediatek Random Number Generator Driver"); MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); +MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>"); MODULE_LICENSE("GPL"); diff --git a/drivers/char/hw_random/n2rng.h b/drivers/char/hw_random/n2rng.h index 9a870f5dc371..7612f15a261f 100644 --- a/drivers/char/hw_random/n2rng.h +++ b/drivers/char/hw_random/n2rng.h @@ -48,7 +48,7 @@ #define HV_RNG_NUM_CONTROL 4 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ extern unsigned long sun4v_rng_get_diag_ctl(void); extern unsigned long sun4v_rng_ctl_read_v1(unsigned long ctl_regs_ra, unsigned long *state, @@ -147,6 +147,6 @@ struct n2rng { #define N2RNG_BUSY_LIMIT 100 #define N2RNG_HCHECK_LIMIT 100 -#endif /* !(__ASSEMBLY__) */ +#endif /* !(__ASSEMBLER__) */ #endif /* _N2RNG_H */ diff --git a/drivers/char/hw_random/npcm-rng.c b/drivers/char/hw_random/npcm-rng.c index 9ff00f096f38..40d6e29dea03 100644 --- a/drivers/char/hw_random/npcm-rng.c +++ b/drivers/char/hw_random/npcm-rng.c @@ -32,6 +32,7 @@ struct npcm_rng { void __iomem *base; struct hwrng rng; + struct device *dev; u32 clkp; }; @@ -57,7 +58,7 @@ static int npcm_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) int retval = 0; int ready; - pm_runtime_get_sync((struct device *)priv->rng.priv); + pm_runtime_get_sync(priv->dev); while (max) { if (wait) { @@ -79,8 +80,7 @@ static int npcm_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) max--; } - pm_runtime_mark_last_busy((struct device *)priv->rng.priv); - pm_runtime_put_sync_autosuspend((struct device *)priv->rng.priv); + pm_runtime_put_sync_autosuspend(priv->dev); return retval || !wait ? retval : -EIO; } @@ -109,7 +109,7 @@ static int npcm_rng_probe(struct platform_device *pdev) #endif priv->rng.name = pdev->name; priv->rng.read = npcm_rng_read; - priv->rng.priv = (unsigned long)&pdev->dev; + priv->dev = &pdev->dev; priv->clkp = (u32)(uintptr_t)of_device_get_match_data(&pdev->dev); writel(NPCM_RNG_M1ROSEL, priv->base + NPCM_RNGMODE_REG); diff --git a/drivers/char/hw_random/omap3-rom-rng.c b/drivers/char/hw_random/omap3-rom-rng.c index 8064c792caf0..aa71f61c3dc9 100644 --- a/drivers/char/hw_random/omap3-rom-rng.c +++ b/drivers/char/hw_random/omap3-rom-rng.c @@ -56,7 +56,6 @@ static int omap3_rom_rng_read(struct hwrng *rng, void *data, size_t max, bool w) else r = 4; - pm_runtime_mark_last_busy(ddata->dev); pm_runtime_put_autosuspend(ddata->dev); return r; diff --git a/drivers/char/hw_random/optee-rng.c b/drivers/char/hw_random/optee-rng.c index 96b5d546d136..72af9f4aa810 100644 --- a/drivers/char/hw_random/optee-rng.c +++ b/drivers/char/hw_random/optee-rng.c @@ -205,15 +205,12 @@ static int get_optee_rng_info(struct device *dev) static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) { - if (ver->impl_id == TEE_IMPL_ID_OPTEE) - return 1; - else - return 0; + return (ver->impl_id == TEE_IMPL_ID_OPTEE); } -static int optee_rng_probe(struct device *dev) +static int optee_rng_probe(struct tee_client_device *rng_device) { - struct tee_client_device *rng_device = to_tee_client_device(dev); + struct device *dev = &rng_device->dev; int ret = 0, err = -ENODEV; struct tee_ioctl_open_session_arg sess_arg; @@ -261,12 +258,10 @@ out_ctx: return err; } -static int optee_rng_remove(struct device *dev) +static void optee_rng_remove(struct tee_client_device *tee_dev) { tee_client_close_session(pvt_data.ctx, pvt_data.session_id); tee_client_close_context(pvt_data.ctx); - - return 0; } static const struct tee_client_device_id optee_rng_id_table[] = { @@ -278,27 +273,15 @@ static const struct tee_client_device_id optee_rng_id_table[] = { MODULE_DEVICE_TABLE(tee, optee_rng_id_table); static struct tee_client_driver optee_rng_driver = { + .probe = optee_rng_probe, + .remove = optee_rng_remove, .id_table = optee_rng_id_table, .driver = { .name = DRIVER_NAME, - .bus = &tee_bus_type, - .probe = optee_rng_probe, - .remove = optee_rng_remove, }, }; -static int __init optee_rng_mod_init(void) -{ - return driver_register(&optee_rng_driver.driver); -} - -static void __exit optee_rng_mod_exit(void) -{ - driver_unregister(&optee_rng_driver.driver); -} - -module_init(optee_rng_mod_init); -module_exit(optee_rng_mod_exit); +module_tee_client_driver(optee_rng_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Sumit Garg <sumit.garg@linaro.org>"); diff --git a/drivers/char/hw_random/pasemi-rng.c b/drivers/char/hw_random/pasemi-rng.c index 6959d6edd44c..d797c6020168 100644 --- a/drivers/char/hw_random/pasemi-rng.c +++ b/drivers/char/hw_random/pasemi-rng.c @@ -9,7 +9,6 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/hw_random.h> #include <linux/delay.h> diff --git a/drivers/char/hw_random/pic32-rng.c b/drivers/char/hw_random/pic32-rng.c index 888e6f5cec1f..1c764924f2dd 100644 --- a/drivers/char/hw_random/pic32-rng.c +++ b/drivers/char/hw_random/pic32-rng.c @@ -12,7 +12,6 @@ #include <linux/hw_random.h> #include <linux/io.h> #include <linux/kernel.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/slab.h> diff --git a/drivers/char/hw_random/powernv-rng.c b/drivers/char/hw_random/powernv-rng.c index 47b88de029f2..df5ba90fdb87 100644 --- a/drivers/char/hw_random/powernv-rng.c +++ b/drivers/char/hw_random/powernv-rng.c @@ -6,7 +6,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/kernel.h> #include <linux/platform_device.h> #include <linux/random.h> diff --git a/drivers/char/hw_random/rockchip-rng.c b/drivers/char/hw_random/rockchip-rng.c index 289b385bbf05..6e3ed4b85605 100644 --- a/drivers/char/hw_random/rockchip-rng.c +++ b/drivers/char/hw_random/rockchip-rng.c @@ -1,12 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 /* - * rockchip-rng.c True Random Number Generator driver for Rockchip RK3568 SoC + * rockchip-rng.c True Random Number Generator driver for Rockchip SoCs * * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd. * Copyright (c) 2022, Aurelien Jarno + * Copyright (c) 2025, Collabora Ltd. * Authors: * Lin Jinhan <troy.lin@rock-chips.com> * Aurelien Jarno <aurelien@aurel32.net> + * Nicolas Frattaroli <nicolas.frattaroli@collabora.com> */ #include <linux/clk.h> #include <linux/hw_random.h> @@ -32,6 +34,9 @@ */ #define RK_RNG_SAMPLE_CNT 1000 +/* after how many bytes of output TRNGv1 implementations should be reseeded */ +#define RK_TRNG_V1_AUTO_RESEED_CNT 16000 + /* TRNG registers from RK3568 TRM-Part2, section 5.4.1 */ #define TRNG_RST_CTL 0x0004 #define TRNG_RNG_CTL 0x0400 @@ -49,11 +54,88 @@ #define TRNG_RNG_SAMPLE_CNT 0x0404 #define TRNG_RNG_DOUT 0x0410 +/* + * TRNG V1 register definitions + * The TRNG V1 IP is a stand-alone TRNG implementation (not part of a crypto IP) + * and can be found in the Rockchip RK3588 SoC + */ +#define TRNG_V1_CTRL 0x0000 +#define TRNG_V1_CTRL_NOP 0x00 +#define TRNG_V1_CTRL_RAND 0x01 +#define TRNG_V1_CTRL_SEED 0x02 + +#define TRNG_V1_STAT 0x0004 +#define TRNG_V1_STAT_SEEDED BIT(9) +#define TRNG_V1_STAT_GENERATING BIT(30) +#define TRNG_V1_STAT_RESEEDING BIT(31) + +#define TRNG_V1_MODE 0x0008 +#define TRNG_V1_MODE_128_BIT (0x00 << 3) +#define TRNG_V1_MODE_256_BIT (0x01 << 3) + +/* Interrupt Enable register; unused because polling is faster */ +#define TRNG_V1_IE 0x0010 +#define TRNG_V1_IE_GLBL_EN BIT(31) +#define TRNG_V1_IE_SEED_DONE_EN BIT(1) +#define TRNG_V1_IE_RAND_RDY_EN BIT(0) + +#define TRNG_V1_ISTAT 0x0014 +#define TRNG_V1_ISTAT_RAND_RDY BIT(0) + +/* RAND0 ~ RAND7 */ +#define TRNG_V1_RAND0 0x0020 +#define TRNG_V1_RAND7 0x003C + +/* Auto Reseed Register */ +#define TRNG_V1_AUTO_RQSTS 0x0060 + +#define TRNG_V1_VERSION 0x00F0 +#define TRNG_v1_VERSION_CODE 0x46bc +/* end of TRNG_V1 register definitions */ + +/* + * RKRNG register definitions + * The RKRNG IP is a stand-alone TRNG implementation (not part of a crypto IP) + * and can be found in the Rockchip RK3576, Rockchip RK3562 and Rockchip RK3528 + * SoCs. It can either output true randomness (TRNG) or "deterministic" + * randomness derived from hashing the true entropy (DRNG). This driver + * implementation uses just the true entropy, and leaves stretching the entropy + * up to Linux. + */ +#define RKRNG_CFG 0x0000 +#define RKRNG_CTRL 0x0010 +#define RKRNG_CTRL_REQ_TRNG BIT(4) +#define RKRNG_STATE 0x0014 +#define RKRNG_STATE_TRNG_RDY BIT(4) +#define RKRNG_TRNG_DATA0 0x0050 +#define RKRNG_TRNG_DATA1 0x0054 +#define RKRNG_TRNG_DATA2 0x0058 +#define RKRNG_TRNG_DATA3 0x005C +#define RKRNG_TRNG_DATA4 0x0060 +#define RKRNG_TRNG_DATA5 0x0064 +#define RKRNG_TRNG_DATA6 0x0068 +#define RKRNG_TRNG_DATA7 0x006C +#define RKRNG_READ_LEN 32 + +/* Before removing this assert, give rk3588_rng_read an upper bound of 32 */ +static_assert(RK_RNG_MAX_BYTE <= (TRNG_V1_RAND7 + 4 - TRNG_V1_RAND0), + "You raised RK_RNG_MAX_BYTE and broke rk3588-rng, congrats."); + struct rk_rng { struct hwrng rng; void __iomem *base; int clk_num; struct clk_bulk_data *clk_bulks; + const struct rk_rng_soc_data *soc_data; + struct device *dev; +}; + +struct rk_rng_soc_data { + int (*rk_rng_init)(struct hwrng *rng); + int (*rk_rng_read)(struct hwrng *rng, void *buf, size_t max, bool wait); + void (*rk_rng_cleanup)(struct hwrng *rng); + unsigned short quality; + bool reset_optional; }; /* The mask in the upper 16 bits determines the bits that are updated */ @@ -62,19 +144,38 @@ static void rk_rng_write_ctl(struct rk_rng *rng, u32 val, u32 mask) writel((mask << 16) | val, rng->base + TRNG_RNG_CTL); } -static int rk_rng_init(struct hwrng *rng) +static inline void rk_rng_writel(struct rk_rng *rng, u32 val, u32 offset) { - struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); - int ret; + writel(val, rng->base + offset); +} + +static inline u32 rk_rng_readl(struct rk_rng *rng, u32 offset) +{ + return readl(rng->base + offset); +} +static int rk_rng_enable_clks(struct rk_rng *rk_rng) +{ + int ret; /* start clocks */ ret = clk_bulk_prepare_enable(rk_rng->clk_num, rk_rng->clk_bulks); if (ret < 0) { - dev_err((struct device *) rk_rng->rng.priv, - "Failed to enable clks %d\n", ret); + dev_err(rk_rng->dev, "Failed to enable clocks: %d\n", ret); return ret; } + return 0; +} + +static int rk3568_rng_init(struct hwrng *rng) +{ + struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); + int ret; + + ret = rk_rng_enable_clks(rk_rng); + if (ret < 0) + return ret; + /* set the sample period */ writel(RK_RNG_SAMPLE_CNT, rk_rng->base + TRNG_RNG_SAMPLE_CNT); @@ -87,7 +188,7 @@ static int rk_rng_init(struct hwrng *rng) return 0; } -static void rk_rng_cleanup(struct hwrng *rng) +static void rk3568_rng_cleanup(struct hwrng *rng) { struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); @@ -98,14 +199,14 @@ static void rk_rng_cleanup(struct hwrng *rng) clk_bulk_disable_unprepare(rk_rng->clk_num, rk_rng->clk_bulks); } -static int rk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) +static int rk3568_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) { struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); size_t to_read = min_t(size_t, max, RK_RNG_MAX_BYTE); u32 reg; int ret = 0; - ret = pm_runtime_resume_and_get((struct device *) rk_rng->rng.priv); + ret = pm_runtime_resume_and_get(rk_rng->dev); if (ret < 0) return ret; @@ -122,12 +223,165 @@ static int rk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) /* Read random data stored in the registers */ memcpy_fromio(buf, rk_rng->base + TRNG_RNG_DOUT, to_read); out: - pm_runtime_mark_last_busy((struct device *) rk_rng->rng.priv); - pm_runtime_put_sync_autosuspend((struct device *) rk_rng->rng.priv); + pm_runtime_put_sync_autosuspend(rk_rng->dev); return (ret < 0) ? ret : to_read; } +static int rk3576_rng_init(struct hwrng *rng) +{ + struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); + + return rk_rng_enable_clks(rk_rng); +} + +static int rk3576_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) +{ + struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); + size_t to_read = min_t(size_t, max, RKRNG_READ_LEN); + int ret = 0; + u32 val; + + ret = pm_runtime_resume_and_get(rk_rng->dev); + if (ret < 0) + return ret; + + rk_rng_writel(rk_rng, RKRNG_CTRL_REQ_TRNG | (RKRNG_CTRL_REQ_TRNG << 16), + RKRNG_CTRL); + + if (readl_poll_timeout(rk_rng->base + RKRNG_STATE, val, + (val & RKRNG_STATE_TRNG_RDY), RK_RNG_POLL_PERIOD_US, + RK_RNG_POLL_TIMEOUT_US)) { + dev_err(rk_rng->dev, "timed out waiting for data\n"); + ret = -ETIMEDOUT; + goto out; + } + + rk_rng_writel(rk_rng, RKRNG_STATE_TRNG_RDY, RKRNG_STATE); + + memcpy_fromio(buf, rk_rng->base + RKRNG_TRNG_DATA0, to_read); + +out: + pm_runtime_put_sync_autosuspend(rk_rng->dev); + + return (ret < 0) ? ret : to_read; +} + +static int rk3588_rng_init(struct hwrng *rng) +{ + struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); + u32 version, status, mask, istat; + int ret; + + ret = rk_rng_enable_clks(rk_rng); + if (ret < 0) + return ret; + + version = rk_rng_readl(rk_rng, TRNG_V1_VERSION); + if (version != TRNG_v1_VERSION_CODE) { + dev_err(rk_rng->dev, + "wrong trng version, expected = %08x, actual = %08x\n", + TRNG_V1_VERSION, version); + ret = -EFAULT; + goto err_disable_clk; + } + + mask = TRNG_V1_STAT_SEEDED | TRNG_V1_STAT_GENERATING | + TRNG_V1_STAT_RESEEDING; + if (readl_poll_timeout(rk_rng->base + TRNG_V1_STAT, status, + (status & mask) == TRNG_V1_STAT_SEEDED, + RK_RNG_POLL_PERIOD_US, RK_RNG_POLL_TIMEOUT_US) < 0) { + dev_err(rk_rng->dev, "timed out waiting for hwrng to reseed\n"); + ret = -ETIMEDOUT; + goto err_disable_clk; + } + + /* + * clear ISTAT flag, downstream advises to do this to avoid + * auto-reseeding "on power on" + */ + istat = rk_rng_readl(rk_rng, TRNG_V1_ISTAT); + rk_rng_writel(rk_rng, istat, TRNG_V1_ISTAT); + + /* auto reseed after RK_TRNG_V1_AUTO_RESEED_CNT bytes */ + rk_rng_writel(rk_rng, RK_TRNG_V1_AUTO_RESEED_CNT / 16, TRNG_V1_AUTO_RQSTS); + + return 0; +err_disable_clk: + clk_bulk_disable_unprepare(rk_rng->clk_num, rk_rng->clk_bulks); + return ret; +} + +static void rk3588_rng_cleanup(struct hwrng *rng) +{ + struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); + + clk_bulk_disable_unprepare(rk_rng->clk_num, rk_rng->clk_bulks); +} + +static int rk3588_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) +{ + struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); + size_t to_read = min_t(size_t, max, RK_RNG_MAX_BYTE); + int ret = 0; + u32 reg; + + ret = pm_runtime_resume_and_get(rk_rng->dev); + if (ret < 0) + return ret; + + /* Clear ISTAT, even without interrupts enabled, this will be updated */ + reg = rk_rng_readl(rk_rng, TRNG_V1_ISTAT); + rk_rng_writel(rk_rng, reg, TRNG_V1_ISTAT); + + /* generate 256 bits of random data */ + rk_rng_writel(rk_rng, TRNG_V1_MODE_256_BIT, TRNG_V1_MODE); + rk_rng_writel(rk_rng, TRNG_V1_CTRL_RAND, TRNG_V1_CTRL); + + ret = readl_poll_timeout_atomic(rk_rng->base + TRNG_V1_ISTAT, reg, + (reg & TRNG_V1_ISTAT_RAND_RDY), 0, + RK_RNG_POLL_TIMEOUT_US); + if (ret < 0) + goto out; + + /* Read random data that's in registers TRNG_V1_RAND0 through RAND7 */ + memcpy_fromio(buf, rk_rng->base + TRNG_V1_RAND0, to_read); + +out: + /* Clear ISTAT */ + rk_rng_writel(rk_rng, reg, TRNG_V1_ISTAT); + /* close the TRNG */ + rk_rng_writel(rk_rng, TRNG_V1_CTRL_NOP, TRNG_V1_CTRL); + + pm_runtime_put_sync_autosuspend(rk_rng->dev); + + return (ret < 0) ? ret : to_read; +} + +static const struct rk_rng_soc_data rk3568_soc_data = { + .rk_rng_init = rk3568_rng_init, + .rk_rng_read = rk3568_rng_read, + .rk_rng_cleanup = rk3568_rng_cleanup, + .quality = 900, + .reset_optional = false, +}; + +static const struct rk_rng_soc_data rk3576_soc_data = { + .rk_rng_init = rk3576_rng_init, + .rk_rng_read = rk3576_rng_read, + .rk_rng_cleanup = rk3588_rng_cleanup, + .quality = 999, /* as determined by actual testing */ + .reset_optional = true, +}; + +static const struct rk_rng_soc_data rk3588_soc_data = { + .rk_rng_init = rk3588_rng_init, + .rk_rng_read = rk3588_rng_read, + .rk_rng_cleanup = rk3588_rng_cleanup, + .quality = 999, /* as determined by actual testing */ + .reset_optional = true, +}; + static int rk_rng_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -139,6 +393,7 @@ static int rk_rng_probe(struct platform_device *pdev) if (!rk_rng) return -ENOMEM; + rk_rng->soc_data = of_device_get_match_data(dev); rk_rng->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(rk_rng->base)) return PTR_ERR(rk_rng->base); @@ -148,34 +403,40 @@ static int rk_rng_probe(struct platform_device *pdev) return dev_err_probe(dev, rk_rng->clk_num, "Failed to get clks property\n"); - rst = devm_reset_control_array_get_exclusive(&pdev->dev); - if (IS_ERR(rst)) - return dev_err_probe(dev, PTR_ERR(rst), "Failed to get reset property\n"); + if (rk_rng->soc_data->reset_optional) + rst = devm_reset_control_array_get_optional_exclusive(dev); + else + rst = devm_reset_control_array_get_exclusive(dev); + + if (rst) { + if (IS_ERR(rst)) + return dev_err_probe(dev, PTR_ERR(rst), "Failed to get reset property\n"); - reset_control_assert(rst); - udelay(2); - reset_control_deassert(rst); + reset_control_assert(rst); + udelay(2); + reset_control_deassert(rst); + } platform_set_drvdata(pdev, rk_rng); rk_rng->rng.name = dev_driver_string(dev); if (!IS_ENABLED(CONFIG_PM)) { - rk_rng->rng.init = rk_rng_init; - rk_rng->rng.cleanup = rk_rng_cleanup; + rk_rng->rng.init = rk_rng->soc_data->rk_rng_init; + rk_rng->rng.cleanup = rk_rng->soc_data->rk_rng_cleanup; } - rk_rng->rng.read = rk_rng_read; - rk_rng->rng.priv = (unsigned long) dev; - rk_rng->rng.quality = 900; + rk_rng->rng.read = rk_rng->soc_data->rk_rng_read; + rk_rng->dev = dev; + rk_rng->rng.quality = rk_rng->soc_data->quality; pm_runtime_set_autosuspend_delay(dev, RK_RNG_AUTOSUSPEND_DELAY); pm_runtime_use_autosuspend(dev); ret = devm_pm_runtime_enable(dev); if (ret) - return dev_err_probe(&pdev->dev, ret, "Runtime pm activation failed.\n"); + return dev_err_probe(dev, ret, "Runtime pm activation failed.\n"); ret = devm_hwrng_register(dev, &rk_rng->rng); if (ret) - return dev_err_probe(&pdev->dev, ret, "Failed to register Rockchip hwrng\n"); + return dev_err_probe(dev, ret, "Failed to register Rockchip hwrng\n"); return 0; } @@ -184,7 +445,7 @@ static int __maybe_unused rk_rng_runtime_suspend(struct device *dev) { struct rk_rng *rk_rng = dev_get_drvdata(dev); - rk_rng_cleanup(&rk_rng->rng); + rk_rng->soc_data->rk_rng_cleanup(&rk_rng->rng); return 0; } @@ -193,7 +454,7 @@ static int __maybe_unused rk_rng_runtime_resume(struct device *dev) { struct rk_rng *rk_rng = dev_get_drvdata(dev); - return rk_rng_init(&rk_rng->rng); + return rk_rng->soc_data->rk_rng_init(&rk_rng->rng); } static const struct dev_pm_ops rk_rng_pm_ops = { @@ -204,7 +465,9 @@ static const struct dev_pm_ops rk_rng_pm_ops = { }; static const struct of_device_id rk_rng_dt_match[] = { - { .compatible = "rockchip,rk3568-rng", }, + { .compatible = "rockchip,rk3568-rng", .data = (void *)&rk3568_soc_data }, + { .compatible = "rockchip,rk3576-rng", .data = (void *)&rk3576_soc_data }, + { .compatible = "rockchip,rk3588-rng", .data = (void *)&rk3588_soc_data }, { /* sentinel */ }, }; @@ -221,8 +484,9 @@ static struct platform_driver rk_rng_driver = { module_platform_driver(rk_rng_driver); -MODULE_DESCRIPTION("Rockchip RK3568 True Random Number Generator driver"); +MODULE_DESCRIPTION("Rockchip True Random Number Generator driver"); MODULE_AUTHOR("Lin Jinhan <troy.lin@rock-chips.com>"); MODULE_AUTHOR("Aurelien Jarno <aurelien@aurel32.net>"); MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>"); +MODULE_AUTHOR("Nicolas Frattaroli <nicolas.frattaroli@collabora.com>"); MODULE_LICENSE("GPL"); diff --git a/drivers/char/hw_random/s390-trng.c b/drivers/char/hw_random/s390-trng.c index d27e32e9bfee..5520f66274b3 100644 --- a/drivers/char/hw_random/s390-trng.c +++ b/drivers/char/hw_random/s390-trng.c @@ -9,8 +9,7 @@ * Author(s): Harald Freudenberger <freude@de.ibm.com> */ -#define KMSG_COMPONENT "trng" -#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#define pr_fmt(fmt) "trng: " fmt #include <linux/hw_random.h> #include <linux/kernel.h> @@ -21,6 +20,7 @@ #include <linux/atomic.h> #include <linux/random.h> #include <linux/sched/signal.h> +#include <linux/slab.h> #include <asm/debug.h> #include <asm/cpacf.h> #include <asm/archrandom.h> @@ -68,7 +68,7 @@ static ssize_t trng_read(struct file *file, char __user *ubuf, */ if (nbytes > sizeof(buf)) { - p = (u8 *) __get_free_page(GFP_KERNEL); + p = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!p) return -ENOMEM; } @@ -95,7 +95,7 @@ static ssize_t trng_read(struct file *file, char __user *ubuf, } if (p != buf) - free_page((unsigned long) p); + kfree(p); DEBUG_DBG("trng_read()=%zd\n", ret); return ret; diff --git a/drivers/char/hw_random/stm32-rng.c b/drivers/char/hw_random/stm32-rng.c index 98edbe796bc5..9a8c00586ab0 100644 --- a/drivers/char/hw_random/stm32-rng.c +++ b/drivers/char/hw_random/stm32-rng.c @@ -255,7 +255,6 @@ static int stm32_rng_read(struct hwrng *rng, void *data, size_t max, bool wait) } exit_rpm: - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_sync_autosuspend(priv->dev); return retval || !wait ? retval : -EIO; diff --git a/drivers/char/hw_random/timeriomem-rng.c b/drivers/char/hw_random/timeriomem-rng.c index b95f6d0f17ed..e61f06393209 100644 --- a/drivers/char/hw_random/timeriomem-rng.c +++ b/drivers/char/hw_random/timeriomem-rng.c @@ -150,7 +150,7 @@ static int timeriomem_rng_probe(struct platform_device *pdev) priv->rng_ops.quality = pdata->quality; } - priv->period = ns_to_ktime(period * NSEC_PER_USEC); + priv->period = us_to_ktime(period); init_completion(&priv->completion); hrtimer_setup(&priv->timer, timeriomem_rng_trigger, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index dd998f4fe4f2..5e83ffa105e4 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -7,10 +7,12 @@ #include <asm/barrier.h> #include <linux/err.h> #include <linux/hw_random.h> +#include <linux/nospec.h> #include <linux/scatterlist.h> #include <linux/spinlock.h> #include <linux/virtio.h> #include <linux/virtio_rng.h> +#include <linux/dma-mapping.h> #include <linux/module.h> #include <linux/slab.h> @@ -28,11 +30,13 @@ struct virtrng_info { unsigned int data_avail; unsigned int data_idx; /* minimal size returned by rng_buffer_size() */ + __dma_from_device_group_begin(); #if SMP_CACHE_BYTES < 32 u8 data[32]; #else u8 data[SMP_CACHE_BYTES]; #endif + __dma_from_device_group_end(); }; static void random_recv_done(struct virtqueue *vq) @@ -66,8 +70,26 @@ static void request_entropy(struct virtrng_info *vi) static unsigned int copy_data(struct virtrng_info *vi, void *buf, unsigned int size) { - size = min_t(unsigned int, size, vi->data_avail); - memcpy(buf, vi->data + vi->data_idx, size); + unsigned int idx, avail; + + /* + * vi->data_avail was set from the device-reported used.len and + * vi->data_idx was advanced by previous copy_data() calls. A + * malicious or buggy virtio-rng backend can drive either past + * sizeof(vi->data). Clamp at point of use and harden the index + * with array_index_nospec() so the memcpy() below cannot be + * steered into adjacent slab memory, including under + * speculation. + */ + avail = min_t(unsigned int, vi->data_avail, sizeof(vi->data)); + if (vi->data_idx >= avail) { + vi->data_avail = 0; + request_entropy(vi); + return 0; + } + size = min_t(unsigned int, size, avail - vi->data_idx); + idx = array_index_nospec(vi->data_idx, sizeof(vi->data)); + memcpy(buf, vi->data + idx, size); vi->data_idx += size; vi->data_avail -= size; if (vi->data_avail == 0) @@ -131,7 +153,7 @@ static int probe_common(struct virtio_device *vdev) int err, index; struct virtrng_info *vi = NULL; - vi = kzalloc(sizeof(struct virtrng_info), GFP_KERNEL); + vi = kzalloc_obj(struct virtrng_info); if (!vi) return -ENOMEM; diff --git a/drivers/char/hw_random/xgene-rng.c b/drivers/char/hw_random/xgene-rng.c index 39acaa503fec..1f4b95341c2e 100644 --- a/drivers/char/hw_random/xgene-rng.c +++ b/drivers/char/hw_random/xgene-rng.c @@ -16,7 +16,6 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/timer.h> @@ -88,12 +87,12 @@ struct xgene_rng_dev { static void xgene_rng_expired_timer(struct timer_list *t) { - struct xgene_rng_dev *ctx = from_timer(ctx, t, failure_timer); + struct xgene_rng_dev *ctx = timer_container_of(ctx, t, failure_timer); /* Clear failure counter as timer expired */ disable_irq(ctx->irq); ctx->failure_cnt = 0; - del_timer(&ctx->failure_timer); + timer_delete(&ctx->failure_timer); enable_irq(ctx->irq); } diff --git a/drivers/char/hw_random/xilinx-trng.c b/drivers/char/hw_random/xilinx-trng.c new file mode 100644 index 000000000000..0fbc22c38fbc --- /dev/null +++ b/drivers/char/hw_random/xilinx-trng.c @@ -0,0 +1,340 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD Versal True Random Number Generator driver + * Copyright (c) 2024 - 2025 Advanced Micro Devices, Inc. + */ + +#include <crypto/sha2.h> +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/firmware/xlnx-zynqmp.h> +#include <linux/hw_random.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +/* TRNG Registers Offsets */ +#define TRNG_STATUS_OFFSET 0x4U +#define TRNG_CTRL_OFFSET 0x8U +#define TRNG_EXT_SEED_OFFSET 0x40U +#define TRNG_PER_STRNG_OFFSET 0x80U +#define TRNG_CORE_OUTPUT_OFFSET 0xC0U +#define TRNG_RESET_OFFSET 0xD0U +#define TRNG_OSC_EN_OFFSET 0xD4U + +/* Mask values */ +#define TRNG_RESET_VAL_MASK BIT(0) +#define TRNG_OSC_EN_VAL_MASK BIT(0) +#define TRNG_CTRL_PRNGSRST_MASK BIT(0) +#define TRNG_CTRL_EUMODE_MASK BIT(8) +#define TRNG_CTRL_TRSSEN_MASK BIT(2) +#define TRNG_CTRL_PRNGSTART_MASK BIT(5) +#define TRNG_CTRL_PRNGXS_MASK BIT(3) +#define TRNG_CTRL_PRNGMODE_MASK BIT(7) +#define TRNG_STATUS_DONE_MASK BIT(0) +#define TRNG_STATUS_QCNT_MASK GENMASK(11, 9) +#define TRNG_STATUS_QCNT_16_BYTES 0x800 + +/* Sizes in bytes */ +#define TRNG_SEED_LEN_BYTES 48U +#define TRNG_SEC_STRENGTH_SHIFT 5U +#define TRNG_SEC_STRENGTH_BYTES BIT(TRNG_SEC_STRENGTH_SHIFT) +#define TRNG_BYTES_PER_REG 4U +#define TRNG_RESET_DELAY 10 +#define TRNG_NUM_INIT_REGS 12U +#define TRNG_READ_4_WORD 4 +#define TRNG_DATA_READ_DELAY 8000 + +struct xilinx_rng { + void __iomem *rng_base; + struct device *dev; + struct hwrng trng; +}; + +static void xtrng_readwrite32(void __iomem *addr, u32 mask, u8 value) +{ + u32 val; + + val = ioread32(addr); + val = (val & (~mask)) | (mask & value); + iowrite32(val, addr); +} + +static void xtrng_trng_reset(void __iomem *addr) +{ + xtrng_readwrite32(addr + TRNG_RESET_OFFSET, TRNG_RESET_VAL_MASK, TRNG_RESET_VAL_MASK); + udelay(TRNG_RESET_DELAY); + xtrng_readwrite32(addr + TRNG_RESET_OFFSET, TRNG_RESET_VAL_MASK, 0); +} + +static void xtrng_hold_reset(void __iomem *addr) +{ + xtrng_readwrite32(addr + TRNG_CTRL_OFFSET, TRNG_CTRL_PRNGSRST_MASK, + TRNG_CTRL_PRNGSRST_MASK); + iowrite32(TRNG_RESET_VAL_MASK, addr + TRNG_RESET_OFFSET); + udelay(TRNG_RESET_DELAY); +} + +static void xtrng_softreset(struct xilinx_rng *rng) +{ + xtrng_readwrite32(rng->rng_base + TRNG_CTRL_OFFSET, TRNG_CTRL_PRNGSRST_MASK, + TRNG_CTRL_PRNGSRST_MASK); + udelay(TRNG_RESET_DELAY); + xtrng_readwrite32(rng->rng_base + TRNG_CTRL_OFFSET, TRNG_CTRL_PRNGSRST_MASK, 0); +} + +/* Return no. of bytes read */ +static size_t xtrng_readblock32(void __iomem *rng_base, __be32 *buf, int blocks32, bool wait) +{ + int read = 0, ret; + int timeout = 1; + int i, idx; + u32 val; + + if (wait) + timeout = TRNG_DATA_READ_DELAY; + + for (i = 0; i < (blocks32 * 2); i++) { + /* TRNG core generate data in 16 bytes. Read twice to complete 32 bytes read */ + ret = readl_poll_timeout(rng_base + TRNG_STATUS_OFFSET, val, + (val & TRNG_STATUS_QCNT_MASK) == + TRNG_STATUS_QCNT_16_BYTES, !!wait, timeout); + if (ret) + break; + + for (idx = 0; idx < TRNG_READ_4_WORD; idx++) { + *(buf + read) = cpu_to_be32(ioread32(rng_base + TRNG_CORE_OUTPUT_OFFSET)); + read += 1; + } + } + return read * 4; +} + +static int xtrng_collect_random_data(struct xilinx_rng *rng, u8 *rand_gen_buf, + int no_of_random_bytes, bool wait) +{ + u8 randbuf[TRNG_SEC_STRENGTH_BYTES]; + int byteleft, blocks, count = 0; + int ret; + + byteleft = no_of_random_bytes & (TRNG_SEC_STRENGTH_BYTES - 1); + blocks = no_of_random_bytes >> TRNG_SEC_STRENGTH_SHIFT; + xtrng_readwrite32(rng->rng_base + TRNG_CTRL_OFFSET, TRNG_CTRL_PRNGSTART_MASK, + TRNG_CTRL_PRNGSTART_MASK); + if (blocks) { + ret = xtrng_readblock32(rng->rng_base, (__be32 *)rand_gen_buf, blocks, wait); + if (!ret) + return 0; + count += ret; + } + + if (byteleft) { + ret = xtrng_readblock32(rng->rng_base, (__be32 *)randbuf, 1, wait); + if (!ret) + return count; + memcpy(rand_gen_buf + (blocks * TRNG_SEC_STRENGTH_BYTES), randbuf, byteleft); + count += byteleft; + } + + xtrng_readwrite32(rng->rng_base + TRNG_CTRL_OFFSET, + TRNG_CTRL_PRNGMODE_MASK | TRNG_CTRL_PRNGSTART_MASK, 0U); + + return count; +} + +static void xtrng_write_multiple_registers(void __iomem *base_addr, u32 *values, size_t n) +{ + void __iomem *reg_addr; + size_t i; + + /* Write seed value into EXTERNAL_SEED Registers in big endian format */ + for (i = 0; i < n; i++) { + reg_addr = (base_addr + ((n - 1 - i) * TRNG_BYTES_PER_REG)); + iowrite32((u32 __force)(cpu_to_be32(values[i])), reg_addr); + } +} + +static void xtrng_enable_entropy(struct xilinx_rng *rng) +{ + iowrite32(TRNG_OSC_EN_VAL_MASK, rng->rng_base + TRNG_OSC_EN_OFFSET); + xtrng_softreset(rng); + iowrite32(TRNG_CTRL_EUMODE_MASK | TRNG_CTRL_TRSSEN_MASK, rng->rng_base + TRNG_CTRL_OFFSET); +} + +static int xtrng_reseed_internal(struct xilinx_rng *rng) +{ + static const u8 default_salt[SHA512_DIGEST_SIZE]; + u8 entropy[SHA512_DIGEST_SIZE] __aligned(4); + u32 val; + int ret; + + xtrng_enable_entropy(rng); + + /* Collect some output from the TRNG. */ + static_assert(sizeof(entropy) >= TRNG_SEED_LEN_BYTES); + ret = xtrng_collect_random_data(rng, entropy, TRNG_SEED_LEN_BYTES, true); + if (ret != TRNG_SEED_LEN_BYTES) + return -EINVAL; + + /* Extract entropy from the TRNG output using HKDF-SHA512-Extract. */ + hmac_sha512_usingrawkey(default_salt, sizeof(default_salt), entropy, + TRNG_SEED_LEN_BYTES, entropy); + + /* Write the extracted entropy to the hardware. */ + xtrng_write_multiple_registers(rng->rng_base + TRNG_EXT_SEED_OFFSET, + (u32 *)entropy, TRNG_NUM_INIT_REGS); + + /* Clear the entropy from the stack. */ + memzero_explicit(entropy, sizeof(entropy)); + + /* select reseed operation */ + iowrite32(TRNG_CTRL_PRNGXS_MASK, rng->rng_base + TRNG_CTRL_OFFSET); + + /* Start the reseed operation with above configuration and wait for STATUS.Done bit to be + * set. Monitor STATUS.CERTF bit, if set indicates SP800-90B entropy health test has failed. + */ + xtrng_readwrite32(rng->rng_base + TRNG_CTRL_OFFSET, TRNG_CTRL_PRNGSTART_MASK, + TRNG_CTRL_PRNGSTART_MASK); + + ret = readl_poll_timeout(rng->rng_base + TRNG_STATUS_OFFSET, val, + (val & TRNG_STATUS_DONE_MASK) == TRNG_STATUS_DONE_MASK, + 1U, 15000U); + if (ret) + return ret; + + xtrng_readwrite32(rng->rng_base + TRNG_CTRL_OFFSET, TRNG_CTRL_PRNGSTART_MASK, 0U); + + return 0; +} + +static int xtrng_random_bytes_generate(struct xilinx_rng *rng, u8 *rand_buf_ptr, + u32 rand_buf_size, int wait) +{ + int nbytes; + int ret; + + xtrng_readwrite32(rng->rng_base + TRNG_CTRL_OFFSET, + TRNG_CTRL_PRNGMODE_MASK | TRNG_CTRL_PRNGXS_MASK, + TRNG_CTRL_PRNGMODE_MASK | TRNG_CTRL_PRNGXS_MASK); + nbytes = xtrng_collect_random_data(rng, rand_buf_ptr, rand_buf_size, wait); + + ret = xtrng_reseed_internal(rng); + if (ret) { + dev_err(rng->dev, "Re-seed fail\n"); + return ret; + } + + return nbytes; +} + +static int xtrng_hwrng_trng_read(struct hwrng *hwrng, void *data, size_t max, bool wait) +{ + u8 buf[TRNG_SEC_STRENGTH_BYTES]; + struct xilinx_rng *rng; + int ret = 0, i = 0; + + rng = container_of(hwrng, struct xilinx_rng, trng); + while (i < max) { + ret = xtrng_random_bytes_generate(rng, buf, TRNG_SEC_STRENGTH_BYTES, wait); + if (ret < 0) { + if (i == 0) + return ret; + break; + } + + memcpy(data + i, buf, min_t(int, ret, (max - i))); + i += min_t(int, ret, (max - i)); + } + return i; +} + +static int xtrng_hwrng_register(struct hwrng *trng) +{ + int ret; + + trng->name = "Xilinx Versal Crypto Engine TRNG"; + trng->read = xtrng_hwrng_trng_read; + + ret = hwrng_register(trng); + if (ret) + pr_err("Fail to register the TRNG\n"); + + return ret; +} + +static void xtrng_hwrng_unregister(struct hwrng *trng) +{ + hwrng_unregister(trng); +} + +static int xtrng_probe(struct platform_device *pdev) +{ + struct xilinx_rng *rng; + int ret; + + rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); + if (!rng) + return -ENOMEM; + + rng->dev = &pdev->dev; + rng->rng_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rng->rng_base)) { + dev_err(&pdev->dev, "Failed to map resource %pe\n", rng->rng_base); + return PTR_ERR(rng->rng_base); + } + + xtrng_trng_reset(rng->rng_base); + ret = xtrng_reseed_internal(rng); + if (ret) { + dev_err(&pdev->dev, "TRNG Seed fail\n"); + return ret; + } + + ret = xtrng_hwrng_register(&rng->trng); + if (ret) { + dev_err(&pdev->dev, "HWRNG device registration failed: %d\n", ret); + return ret; + } + platform_set_drvdata(pdev, rng); + + return 0; +} + +static void xtrng_remove(struct platform_device *pdev) +{ + struct xilinx_rng *rng; + u32 zero[TRNG_NUM_INIT_REGS] = { }; + + rng = platform_get_drvdata(pdev); + xtrng_hwrng_unregister(&rng->trng); + xtrng_write_multiple_registers(rng->rng_base + TRNG_EXT_SEED_OFFSET, zero, + TRNG_NUM_INIT_REGS); + xtrng_write_multiple_registers(rng->rng_base + TRNG_PER_STRNG_OFFSET, zero, + TRNG_NUM_INIT_REGS); + xtrng_hold_reset(rng->rng_base); +} + +static const struct of_device_id xtrng_of_match[] = { + { .compatible = "xlnx,versal-trng", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, xtrng_of_match); + +static struct platform_driver xtrng_driver = { + .driver = { + .name = "xlnx,versal-trng", + .of_match_table = xtrng_of_match, + }, + .probe = xtrng_probe, + .remove = xtrng_remove, +}; + +module_platform_driver(xtrng_driver); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Harsh Jain <h.jain@amd.com>"); +MODULE_AUTHOR("Mounika Botcha <mounika.botcha@amd.com>"); +MODULE_DESCRIPTION("True Random Number Generator Driver"); diff --git a/drivers/char/hw_random/xiphera-trng.c b/drivers/char/hw_random/xiphera-trng.c index 4af64f76c8d6..ab5d852ff69f 100644 --- a/drivers/char/hw_random/xiphera-trng.c +++ b/drivers/char/hw_random/xiphera-trng.c @@ -3,7 +3,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/err.h> #include <linux/io.h> #include <linux/hw_random.h> diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index f4adc6feb3b2..669f76000197 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -84,6 +84,13 @@ config IPMI_IPMB bus, and it also supports direct messaging on the bus using IPMB direct messages. This module requires I2C support. +config IPMI_LS2K + bool 'Loongson-2K IPMI interface' + depends on LOONGARCH + select MFD_LS2K_BMC_CORE + help + Provides a driver for Loongson-2K IPMI interfaces. + config IPMI_POWERNV depends on PPC_POWERNV tristate 'POWERNV (OPAL firmware) IPMI interface' @@ -180,6 +187,16 @@ config SSIF_IPMI_BMC The driver implements the BMC side of the SMBus system interface (SSIF). +config SSIF_IPMI_BMC_KUNIT_TEST + bool "KUnit tests for SSIF IPMI BMC driver" if !KUNIT_ALL_TESTS + depends on KUNIT=y + depends on SSIF_IPMI_BMC + default KUNIT_ALL_TESTS + help + This option builds unit tests that exercise the SSIF BMC state + machine, including request handling, response transmission, + and error paths such as aborted or truncated transfers. + config IPMB_DEVICE_INTERFACE tristate 'IPMB Interface handler' depends on I2C diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile index e0944547c9d0..4ea450a82242 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -8,6 +8,7 @@ ipmi_si-y := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o ipmi_bt_sm.o \ ipmi_si_mem_io.o ipmi_si-$(CONFIG_HAS_IOPORT) += ipmi_si_port_io.o ipmi_si-$(CONFIG_PCI) += ipmi_si_pci.o +ipmi_si-$(CONFIG_IPMI_LS2K) += ipmi_si_ls2k.o ipmi_si-$(CONFIG_PARISC) += ipmi_si_parisc.o obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o diff --git a/drivers/char/ipmi/bt-bmc.c b/drivers/char/ipmi/bt-bmc.c index 009e32033b17..a179d4797011 100644 --- a/drivers/char/ipmi/bt-bmc.c +++ b/drivers/char/ipmi/bt-bmc.c @@ -347,7 +347,7 @@ static const struct file_operations bt_bmc_fops = { static void poll_timer(struct timer_list *t) { - struct bt_bmc *bt_bmc = from_timer(bt_bmc, t, poll_timer); + struct bt_bmc *bt_bmc = timer_container_of(bt_bmc, t, poll_timer); bt_bmc->poll_timer.expires += msecs_to_jiffies(500); wake_up(&bt_bmc->queue); @@ -465,7 +465,7 @@ static void bt_bmc_remove(struct platform_device *pdev) misc_deregister(&bt_bmc->miscdev); if (bt_bmc->irq < 0) - del_timer_sync(&bt_bmc->poll_timer); + timer_delete_sync(&bt_bmc->poll_timer); } static const struct of_device_id bt_bmc_match[] = { diff --git a/drivers/char/ipmi/ipmb_dev_int.c b/drivers/char/ipmi/ipmb_dev_int.c index ee2bdc7ed0da..680ff15c30ab 100644 --- a/drivers/char/ipmi/ipmb_dev_int.c +++ b/drivers/char/ipmi/ipmb_dev_int.c @@ -208,7 +208,7 @@ static void ipmb_handle_request(struct ipmb_dev *ipmb_dev) REQUEST_QUEUE_MAX_LEN) return; - queue_elem = kmalloc(sizeof(*queue_elem), GFP_ATOMIC); + queue_elem = kmalloc_obj(*queue_elem, GFP_ATOMIC); if (!queue_elem) return; @@ -353,8 +353,8 @@ static void ipmb_remove(struct i2c_client *client) } static const struct i2c_device_id ipmb_id[] = { - { "ipmb-dev" }, - {} + { .name = "ipmb-dev" }, + { } }; MODULE_DEVICE_TABLE(i2c, ipmb_id); diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c index e6ba35b71f10..38d0f1bc10b0 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c @@ -90,7 +90,7 @@ static int ipmi_open(struct inode *inode, struct file *file) int rv; struct ipmi_file_private *priv; - priv = kmalloc(sizeof(*priv), GFP_KERNEL); + priv = kmalloc_obj(*priv); if (!priv) return -ENOMEM; @@ -813,7 +813,7 @@ static void ipmi_new_smi(int if_num, struct device *device) dev_t dev = MKDEV(ipmi_major, if_num); struct ipmi_reg_list *entry; - entry = kmalloc(sizeof(*entry), GFP_KERNEL); + entry = kmalloc_obj(*entry); if (!entry) { pr_err("ipmi_devintf: Unable to create the ipmi class device link\n"); return; diff --git a/drivers/char/ipmi/ipmi_dmi.c b/drivers/char/ipmi/ipmi_dmi.c index bbf7029e224b..505e32911c34 100644 --- a/drivers/char/ipmi/ipmi_dmi.c +++ b/drivers/char/ipmi/ipmi_dmi.c @@ -74,7 +74,7 @@ static void __init dmi_add_platform_ipmi(unsigned long base_addr, p.slave_addr = slave_addr; p.addr_source = SI_SMBIOS; - info = kmalloc(sizeof(*info), GFP_KERNEL); + info = kmalloc_obj(*info); if (!info) { pr_warn("Could not allocate dmi info\n"); } else { diff --git a/drivers/char/ipmi/ipmi_ipmb.c b/drivers/char/ipmi/ipmi_ipmb.c index 6a4f279c7c1f..1f1e5718f082 100644 --- a/drivers/char/ipmi/ipmi_ipmb.c +++ b/drivers/char/ipmi/ipmi_ipmb.c @@ -202,11 +202,16 @@ static int ipmi_ipmb_slave_cb(struct i2c_client *client, break; case I2C_SLAVE_READ_REQUESTED: + *val = 0xff; + ipmi_ipmb_check_msg_done(iidev); + break; + case I2C_SLAVE_STOP: ipmi_ipmb_check_msg_done(iidev); break; case I2C_SLAVE_READ_PROCESSED: + *val = 0xff; break; } @@ -404,8 +409,7 @@ static void ipmi_ipmb_shutdown(void *send_info) ipmi_ipmb_stop_thread(iidev); } -static void ipmi_ipmb_sender(void *send_info, - struct ipmi_smi_msg *msg) +static int ipmi_ipmb_sender(void *send_info, struct ipmi_smi_msg *msg) { struct ipmi_ipmb_dev *iidev = send_info; unsigned long flags; @@ -417,6 +421,7 @@ static void ipmi_ipmb_sender(void *send_info, spin_unlock_irqrestore(&iidev->lock, flags); up(&iidev->wake_thread); + return IPMI_CC_NO_ERROR; } static void ipmi_ipmb_request_events(void *send_info) @@ -561,8 +566,8 @@ MODULE_DEVICE_TABLE(of, of_ipmi_ipmb_match); #endif static const struct i2c_device_id ipmi_ipmb_id[] = { - { DEVICE_NAME }, - {} + { .name = DEVICE_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, ipmi_ipmb_id); diff --git a/drivers/char/ipmi/ipmi_kcs_sm.c b/drivers/char/ipmi/ipmi_kcs_sm.c index ecfcb50302f6..c89055db39e5 100644 --- a/drivers/char/ipmi/ipmi_kcs_sm.c +++ b/drivers/char/ipmi/ipmi_kcs_sm.c @@ -102,7 +102,7 @@ enum kcs_states { /* Timeouts in microseconds. */ #define IBF_RETRY_TIMEOUT (5*USEC_PER_SEC) #define OBF_RETRY_TIMEOUT (5*USEC_PER_SEC) -#define MAX_ERROR_RETRIES 10 +#define MAX_ERROR_RETRIES 2 #define ERROR0_OBF_WAIT_JIFFIES (2*HZ) struct si_sm_data { @@ -122,10 +122,10 @@ struct si_sm_data { unsigned long error0_timeout; }; -static unsigned int init_kcs_data_with_state(struct si_sm_data *kcs, - struct si_sm_io *io, enum kcs_states state) +static unsigned int init_kcs_data(struct si_sm_data *kcs, + struct si_sm_io *io) { - kcs->state = state; + kcs->state = KCS_IDLE; kcs->io = io; kcs->write_pos = 0; kcs->write_count = 0; @@ -140,12 +140,6 @@ static unsigned int init_kcs_data_with_state(struct si_sm_data *kcs, return 2; } -static unsigned int init_kcs_data(struct si_sm_data *kcs, - struct si_sm_io *io) -{ - return init_kcs_data_with_state(kcs, io, KCS_IDLE); -} - static inline unsigned char read_status(struct si_sm_data *kcs) { return kcs->io->inputb(kcs->io, 1); @@ -276,7 +270,7 @@ static int start_kcs_transaction(struct si_sm_data *kcs, unsigned char *data, if (size > MAX_KCS_WRITE_SIZE) return IPMI_REQ_LEN_EXCEEDED_ERR; - if (kcs->state != KCS_IDLE) { + if ((kcs->state != KCS_IDLE) && (kcs->state != KCS_HOSED)) { dev_warn(kcs->io->dev, "KCS in invalid state %d\n", kcs->state); return IPMI_NOT_IN_MY_STATE_ERR; } @@ -501,7 +495,7 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time) } if (kcs->state == KCS_HOSED) { - init_kcs_data_with_state(kcs, kcs->io, KCS_ERROR0); + init_kcs_data(kcs, kcs->io); return SI_SM_HOSED; } diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 1e5313748f8b..ab4c85f3d6fe 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -27,7 +27,6 @@ #include <linux/ipmi_smi.h> #include <linux/notifier.h> #include <linux/init.h> -#include <linux/proc_fs.h> #include <linux/rcupdate.h> #include <linux/interrupt.h> #include <linux/moduleparam.h> @@ -39,17 +38,22 @@ #define IPMI_DRIVER_VERSION "39.2" -static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void); +static struct ipmi_recv_msg *ipmi_alloc_recv_msg(struct ipmi_user *user); +static void ipmi_set_recv_msg_user(struct ipmi_recv_msg *msg, + struct ipmi_user *user); static int ipmi_init_msghandler(void); -static void smi_recv_work(struct work_struct *t); +static void smi_work(struct work_struct *t); static void handle_new_recv_msgs(struct ipmi_smi *intf); static void need_waiter(struct ipmi_smi *intf); static int handle_one_recv_msg(struct ipmi_smi *intf, struct ipmi_smi_msg *msg); +static void intf_free(struct kref *ref); static bool initialized; static bool drvregistered; +static struct timer_list ipmi_timer; + /* Numbers in this enumerator should be mapped to ipmi_panic_event_str */ enum ipmi_panic_event_op { IPMI_SEND_PANIC_EVENT_NONE, @@ -180,14 +184,8 @@ MODULE_PARM_DESC(max_msgs_per_user, struct ipmi_user { struct list_head link; - /* - * Set to NULL when the user is destroyed, a pointer to myself - * so srcu_dereference can be used on it. - */ - struct ipmi_user *self; - struct srcu_struct release_barrier; - struct kref refcount; + refcount_t destroyed; /* The upper layer that handles receive messages. */ const struct ipmi_user_hndl *handler; @@ -200,30 +198,8 @@ struct ipmi_user { bool gets_events; atomic_t nr_msgs; - - /* Free must run in process context for RCU cleanup. */ - struct work_struct remove_work; }; -static struct workqueue_struct *remove_work_wq; - -static struct ipmi_user *acquire_ipmi_user(struct ipmi_user *user, int *index) - __acquires(user->release_barrier) -{ - struct ipmi_user *ruser; - - *index = srcu_read_lock(&user->release_barrier); - ruser = srcu_dereference(user->self, &user->release_barrier); - if (!ruser) - srcu_read_unlock(&user->release_barrier, *index); - return ruser; -} - -static void release_ipmi_user(struct ipmi_user *user, int index) -{ - srcu_read_unlock(&user->release_barrier, index); -} - struct cmd_rcvr { struct list_head link; @@ -327,6 +303,8 @@ struct bmc_device { }; #define to_bmc_device(x) container_of((x), struct bmc_device, pdev.dev) +static struct workqueue_struct *bmc_remove_work_wq; + static int bmc_get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc, struct ipmi_device_id *id, bool *guid_set, guid_t *guid); @@ -451,14 +429,14 @@ struct ipmi_smi { struct list_head link; /* - * The list of upper layers that are using me. seq_lock write - * protects this. Read protection is with srcu. + * The list of upper layers that are using me. */ struct list_head users; - struct srcu_struct users_srcu; + struct mutex users_mutex; atomic_t nr_users; struct device_attribute nr_users_devattr; struct device_attribute nr_msgs_devattr; + struct device_attribute maintenance_mode_devattr; /* Used for wake ups at startup. */ @@ -491,20 +469,27 @@ struct ipmi_smi { * interface to match them up with their responses. A routine * is called periodically to time the items in this list. */ - spinlock_t seq_lock; + struct mutex seq_lock; struct seq_table seq_table[IPMI_IPMB_NUM_SEQ]; int curr_seq; /* - * Messages queued for delivery. If delivery fails (out of memory - * for instance), They will stay in here to be processed later in a - * periodic timer interrupt. The workqueue is for handling received - * messages directly from the handler. + * Messages queued for deliver to the user. + */ + struct mutex user_msgs_mutex; + struct list_head user_msgs; + + /* + * Messages queued for processing. If processing fails (out + * of memory for instance), They will stay in here to be + * processed later in a periodic timer interrupt. The + * workqueue is for handling received messages directly from + * the handler. */ spinlock_t waiting_rcv_msgs_lock; struct list_head waiting_rcv_msgs; atomic_t watchdog_pretimeouts_to_deliver; - struct work_struct recv_work; + struct work_struct smi_work; spinlock_t xmit_msgs_lock; struct list_head xmit_msgs; @@ -522,10 +507,9 @@ struct ipmi_smi { * Events that were queues because no one was there to receive * them. */ - spinlock_t events_lock; /* For dealing with event stuff. */ + struct mutex events_mutex; /* For dealing with event stuff. */ struct list_head waiting_events; unsigned int waiting_events_count; /* How many events in queue? */ - char delivering_events; char event_msg_printed; /* How many users are waiting for events? */ @@ -560,7 +544,11 @@ struct ipmi_smi { /* For handling of maintenance mode. */ int maintenance_mode; - bool maintenance_mode_enable; + +#define IPMI_MAINTENANCE_MODE_STATE_OFF 0 +#define IPMI_MAINTENANCE_MODE_STATE_FIRMWARE 1 +#define IPMI_MAINTENANCE_MODE_STATE_RESET 2 + int maintenance_mode_state; int auto_maintenance_timeout; spinlock_t maintenance_mode_lock; /* Used in a timer... */ @@ -611,8 +599,47 @@ static void __ipmi_bmc_unregister(struct ipmi_smi *intf); static int __ipmi_bmc_register(struct ipmi_smi *intf, struct ipmi_device_id *id, bool guid_set, guid_t *guid, int intf_num); -static int __scan_channels(struct ipmi_smi *intf, struct ipmi_device_id *id); +static int __scan_channels(struct ipmi_smi *intf, + struct ipmi_device_id *id, bool rescan); + +static void ipmi_lock_xmit_msgs(struct ipmi_smi *intf, int run_to_completion, + unsigned long *flags) +{ + if (run_to_completion) + return; + spin_lock_irqsave(&intf->xmit_msgs_lock, *flags); +} +static void ipmi_unlock_xmit_msgs(struct ipmi_smi *intf, int run_to_completion, + unsigned long *flags) +{ + if (run_to_completion) + return; + spin_unlock_irqrestore(&intf->xmit_msgs_lock, *flags); +} + +static void free_ipmi_user(struct kref *ref) +{ + struct ipmi_user *user = container_of(ref, struct ipmi_user, refcount); + struct module *owner; + + owner = user->intf->owner; + kref_put(&user->intf->refcount, intf_free); + module_put(owner); + vfree(user); +} + +static void release_ipmi_user(struct ipmi_user *user) +{ + kref_put(&user->refcount, free_ipmi_user); +} + +static struct ipmi_user *acquire_ipmi_user(struct ipmi_user *user) +{ + if (!kref_get_unless_zero(&user->refcount)) + return NULL; + return user; +} /* * The driver model view of the IPMI messaging driver. @@ -630,9 +657,6 @@ static DEFINE_MUTEX(ipmidriver_mutex); static LIST_HEAD(ipmi_interfaces); static DEFINE_MUTEX(ipmi_interfaces_mutex); -#define ipmi_interfaces_mutex_held() \ - lockdep_is_held(&ipmi_interfaces_mutex) -static struct srcu_struct ipmi_interfaces_srcu; /* * List of watchers that want to know when smi's are added and deleted. @@ -698,27 +722,20 @@ static void free_smi_msg_list(struct list_head *q) } } -static void clean_up_interface_data(struct ipmi_smi *intf) +static void intf_free(struct kref *ref) { + struct ipmi_smi *intf = container_of(ref, struct ipmi_smi, refcount); int i; struct cmd_rcvr *rcvr, *rcvr2; - struct list_head list; - - cancel_work_sync(&intf->recv_work); free_smi_msg_list(&intf->waiting_rcv_msgs); free_recv_msg_list(&intf->waiting_events); /* * Wholesale remove all the entries from the list in the - * interface and wait for RCU to know that none are in use. + * interface. No need for locks, this is single-threaded. */ - mutex_lock(&intf->cmd_rcvrs_mutex); - INIT_LIST_HEAD(&list); - list_splice_init_rcu(&intf->cmd_rcvrs, &list, synchronize_rcu); - mutex_unlock(&intf->cmd_rcvrs_mutex); - - list_for_each_entry_safe(rcvr, rcvr2, &list, link) + list_for_each_entry_safe(rcvr, rcvr2, &intf->cmd_rcvrs, link) kfree(rcvr); for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) { @@ -726,20 +743,17 @@ static void clean_up_interface_data(struct ipmi_smi *intf) && (intf->seq_table[i].recv_msg)) ipmi_free_recv_msg(intf->seq_table[i].recv_msg); } -} - -static void intf_free(struct kref *ref) -{ - struct ipmi_smi *intf = container_of(ref, struct ipmi_smi, refcount); - clean_up_interface_data(intf); kfree(intf); } int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher) { struct ipmi_smi *intf; - int index, rv; + unsigned int count = 0, i; + int *interfaces = NULL; + struct device **devices = NULL; + int rv = 0; /* * Make sure the driver is actually initialized, this handles @@ -753,20 +767,51 @@ int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher) list_add(&watcher->link, &smi_watchers); - index = srcu_read_lock(&ipmi_interfaces_srcu); - list_for_each_entry_rcu(intf, &ipmi_interfaces, link, - lockdep_is_held(&smi_watchers_mutex)) { - int intf_num = READ_ONCE(intf->intf_num); + /* + * Build an array of ipmi interfaces and fill it in, and + * another array of the devices. We can't call the callback + * with ipmi_interfaces_mutex held. smi_watchers_mutex will + * keep things in order for the user. + */ + mutex_lock(&ipmi_interfaces_mutex); + list_for_each_entry(intf, &ipmi_interfaces, link) + count++; + if (count > 0) { + interfaces = kmalloc_objs(*interfaces, count); + if (!interfaces) { + rv = -ENOMEM; + } else { + devices = kmalloc_objs(*devices, count); + if (!devices) { + kfree(interfaces); + interfaces = NULL; + rv = -ENOMEM; + } + } + count = 0; + } + if (interfaces) { + list_for_each_entry(intf, &ipmi_interfaces, link) { + int intf_num = READ_ONCE(intf->intf_num); + + if (intf_num == -1) + continue; + devices[count] = intf->si_dev; + interfaces[count++] = intf_num; + } + } + mutex_unlock(&ipmi_interfaces_mutex); - if (intf_num == -1) - continue; - watcher->new_smi(intf_num, intf->si_dev); + if (interfaces) { + for (i = 0; i < count; i++) + watcher->new_smi(interfaces[i], devices[i]); + kfree(interfaces); + kfree(devices); } - srcu_read_unlock(&ipmi_interfaces_srcu, index); mutex_unlock(&smi_watchers_mutex); - return 0; + return rv; } EXPORT_SYMBOL(ipmi_smi_watcher_register); @@ -779,22 +824,17 @@ int ipmi_smi_watcher_unregister(struct ipmi_smi_watcher *watcher) } EXPORT_SYMBOL(ipmi_smi_watcher_unregister); -/* - * Must be called with smi_watchers_mutex held. - */ static void call_smi_watchers(int i, struct device *dev) { struct ipmi_smi_watcher *w; - mutex_lock(&smi_watchers_mutex); list_for_each_entry(w, &smi_watchers, link) { if (try_module_get(w->owner)) { w->new_smi(i, dev); module_put(w->owner); } } - mutex_unlock(&smi_watchers_mutex); } static int @@ -939,20 +979,15 @@ static int deliver_response(struct ipmi_smi *intf, struct ipmi_recv_msg *msg) * risk. At this moment, simply skip it in that case. */ ipmi_free_recv_msg(msg); - atomic_dec(&msg->user->nr_msgs); } else { - int index; - struct ipmi_user *user = acquire_ipmi_user(msg->user, &index); - - if (user) { - atomic_dec(&user->nr_msgs); - user->handler->ipmi_recv_hndl(msg, user->handler_data); - release_ipmi_user(user, index); - } else { - /* User went away, give up. */ - ipmi_free_recv_msg(msg); - rv = -EINVAL; - } + /* + * Deliver it in smi_work. The message will hold a + * refcount to the user. + */ + mutex_lock(&intf->user_msgs_mutex); + list_add_tail(&msg->link, &intf->user_msgs); + mutex_unlock(&intf->user_msgs_mutex); + queue_work(system_percpu_wq, &intf->smi_work); } return rv; @@ -1104,12 +1139,11 @@ static int intf_find_seq(struct ipmi_smi *intf, struct ipmi_recv_msg **recv_msg) { int rv = -ENODEV; - unsigned long flags; if (seq >= IPMI_IPMB_NUM_SEQ) return -EINVAL; - spin_lock_irqsave(&intf->seq_lock, flags); + mutex_lock(&intf->seq_lock); if (intf->seq_table[seq].inuse) { struct ipmi_recv_msg *msg = intf->seq_table[seq].recv_msg; @@ -1122,7 +1156,7 @@ static int intf_find_seq(struct ipmi_smi *intf, rv = 0; } } - spin_unlock_irqrestore(&intf->seq_lock, flags); + mutex_unlock(&intf->seq_lock); return rv; } @@ -1133,14 +1167,13 @@ static int intf_start_seq_timer(struct ipmi_smi *intf, long msgid) { int rv = -ENODEV; - unsigned long flags; unsigned char seq; unsigned long seqid; GET_SEQ_FROM_MSGID(msgid, seq, seqid); - spin_lock_irqsave(&intf->seq_lock, flags); + mutex_lock(&intf->seq_lock); /* * We do this verification because the user can be deleted * while a message is outstanding. @@ -1151,7 +1184,7 @@ static int intf_start_seq_timer(struct ipmi_smi *intf, ent->timeout = ent->orig_timeout; rv = 0; } - spin_unlock_irqrestore(&intf->seq_lock, flags); + mutex_unlock(&intf->seq_lock); return rv; } @@ -1162,7 +1195,6 @@ static int intf_err_seq(struct ipmi_smi *intf, unsigned int err) { int rv = -ENODEV; - unsigned long flags; unsigned char seq; unsigned long seqid; struct ipmi_recv_msg *msg = NULL; @@ -1170,7 +1202,7 @@ static int intf_err_seq(struct ipmi_smi *intf, GET_SEQ_FROM_MSGID(msgid, seq, seqid); - spin_lock_irqsave(&intf->seq_lock, flags); + mutex_lock(&intf->seq_lock); /* * We do this verification because the user can be deleted * while a message is outstanding. @@ -1184,7 +1216,7 @@ static int intf_err_seq(struct ipmi_smi *intf, msg = ent->recv_msg; rv = 0; } - spin_unlock_irqrestore(&intf->seq_lock, flags); + mutex_unlock(&intf->seq_lock); if (msg) deliver_err_response(intf, msg, err); @@ -1192,23 +1224,13 @@ static int intf_err_seq(struct ipmi_smi *intf, return rv; } -static void free_user_work(struct work_struct *work) -{ - struct ipmi_user *user = container_of(work, struct ipmi_user, - remove_work); - - cleanup_srcu_struct(&user->release_barrier); - vfree(user); -} - int ipmi_create_user(unsigned int if_num, const struct ipmi_user_hndl *handler, void *handler_data, struct ipmi_user **user) { - unsigned long flags; - struct ipmi_user *new_user; - int rv, index; + struct ipmi_user *new_user = NULL; + int rv = 0; struct ipmi_smi *intf; /* @@ -1230,30 +1252,31 @@ int ipmi_create_user(unsigned int if_num, if (rv) return rv; - new_user = vzalloc(sizeof(*new_user)); - if (!new_user) - return -ENOMEM; - - index = srcu_read_lock(&ipmi_interfaces_srcu); - list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { + mutex_lock(&ipmi_interfaces_mutex); + list_for_each_entry(intf, &ipmi_interfaces, link) { if (intf->intf_num == if_num) goto found; } /* Not found, return an error */ rv = -EINVAL; - goto out_kfree; + goto out_unlock; found: + if (intf->in_shutdown) { + rv = -ENODEV; + goto out_unlock; + } + if (atomic_add_return(1, &intf->nr_users) > max_users) { rv = -EBUSY; goto out_kfree; } - INIT_WORK(&new_user->remove_work, free_user_work); - - rv = init_srcu_struct(&new_user->release_barrier); - if (rv) + new_user = vzalloc(sizeof(*new_user)); + if (!new_user) { + rv = -ENOMEM; goto out_kfree; + } if (!try_module_get(intf->owner)) { rv = -ENODEV; @@ -1265,86 +1288,68 @@ int ipmi_create_user(unsigned int if_num, atomic_set(&new_user->nr_msgs, 0); kref_init(&new_user->refcount); + refcount_set(&new_user->destroyed, 1); + kref_get(&new_user->refcount); /* Destroy owns a refcount. */ new_user->handler = handler; new_user->handler_data = handler_data; new_user->intf = intf; new_user->gets_events = false; - rcu_assign_pointer(new_user->self, new_user); - spin_lock_irqsave(&intf->seq_lock, flags); - list_add_rcu(&new_user->link, &intf->users); - spin_unlock_irqrestore(&intf->seq_lock, flags); + mutex_lock(&intf->users_mutex); + mutex_lock(&intf->seq_lock); + list_add(&new_user->link, &intf->users); + mutex_unlock(&intf->seq_lock); + mutex_unlock(&intf->users_mutex); + if (handler->ipmi_watchdog_pretimeout) /* User wants pretimeouts, so make sure to watch for them. */ smi_add_watch(intf, IPMI_WATCH_MASK_CHECK_WATCHDOG); - srcu_read_unlock(&ipmi_interfaces_srcu, index); - *user = new_user; - return 0; out_kfree: - atomic_dec(&intf->nr_users); - srcu_read_unlock(&ipmi_interfaces_srcu, index); - vfree(new_user); + if (rv) { + atomic_dec(&intf->nr_users); + vfree(new_user); + } else { + *user = new_user; + } +out_unlock: + mutex_unlock(&ipmi_interfaces_mutex); return rv; } EXPORT_SYMBOL(ipmi_create_user); int ipmi_get_smi_info(int if_num, struct ipmi_smi_info *data) { - int rv, index; + int rv = -EINVAL; struct ipmi_smi *intf; - index = srcu_read_lock(&ipmi_interfaces_srcu); - list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { - if (intf->intf_num == if_num) - goto found; + mutex_lock(&ipmi_interfaces_mutex); + list_for_each_entry(intf, &ipmi_interfaces, link) { + if (intf->intf_num == if_num) { + if (!intf->handlers->get_smi_info) + rv = -ENOTTY; + else + rv = intf->handlers->get_smi_info(intf->send_info, data); + break; + } } - srcu_read_unlock(&ipmi_interfaces_srcu, index); - - /* Not found, return an error */ - return -EINVAL; - -found: - if (!intf->handlers->get_smi_info) - rv = -ENOTTY; - else - rv = intf->handlers->get_smi_info(intf->send_info, data); - srcu_read_unlock(&ipmi_interfaces_srcu, index); + mutex_unlock(&ipmi_interfaces_mutex); return rv; } EXPORT_SYMBOL(ipmi_get_smi_info); -static void free_user(struct kref *ref) -{ - struct ipmi_user *user = container_of(ref, struct ipmi_user, refcount); - - /* SRCU cleanup must happen in workqueue context. */ - queue_work(remove_work_wq, &user->remove_work); -} - +/* Must be called with intf->users_mutex held. */ static void _ipmi_destroy_user(struct ipmi_user *user) { struct ipmi_smi *intf = user->intf; int i; - unsigned long flags; struct cmd_rcvr *rcvr; struct cmd_rcvr *rcvrs = NULL; - struct module *owner; + struct ipmi_recv_msg *msg, *msg2; - if (!acquire_ipmi_user(user, &i)) { - /* - * The user has already been cleaned up, just make sure - * nothing is using it and return. - */ - synchronize_srcu(&user->release_barrier); + if (!refcount_dec_if_one(&user->destroyed)) return; - } - - rcu_assign_pointer(user->self, NULL); - release_ipmi_user(user, i); - - synchronize_srcu(&user->release_barrier); if (user->handler->shutdown) user->handler->shutdown(user->handler_data); @@ -1355,11 +1360,11 @@ static void _ipmi_destroy_user(struct ipmi_user *user) if (user->gets_events) atomic_dec(&intf->event_waiters); - /* Remove the user from the interface's sequence table. */ - spin_lock_irqsave(&intf->seq_lock, flags); - list_del_rcu(&user->link); + /* Remove the user from the interface's list and sequence table. */ + list_del(&user->link); atomic_dec(&intf->nr_users); + mutex_lock(&intf->seq_lock); for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) { if (intf->seq_table[i].inuse && (intf->seq_table[i].recv_msg->user == user)) { @@ -1368,13 +1373,13 @@ static void _ipmi_destroy_user(struct ipmi_user *user) ipmi_free_recv_msg(intf->seq_table[i].recv_msg); } } - spin_unlock_irqrestore(&intf->seq_lock, flags); + mutex_unlock(&intf->seq_lock); /* * Remove the user from the command receiver's table. First * we build a list of everything (not using the standard link, * since other things may be using it till we do - * synchronize_srcu()) then free everything in that list. + * synchronize_rcu()) then free everything in that list. */ mutex_lock(&intf->cmd_rcvrs_mutex); list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link, @@ -1386,23 +1391,33 @@ static void _ipmi_destroy_user(struct ipmi_user *user) } } mutex_unlock(&intf->cmd_rcvrs_mutex); - synchronize_rcu(); while (rcvrs) { rcvr = rcvrs; rcvrs = rcvr->next; kfree(rcvr); } - owner = intf->owner; - kref_put(&intf->refcount, intf_free); - module_put(owner); + mutex_lock(&intf->user_msgs_mutex); + list_for_each_entry_safe(msg, msg2, &intf->user_msgs, link) { + if (msg->user != user) + continue; + list_del(&msg->link); + ipmi_free_recv_msg(msg); + } + mutex_unlock(&intf->user_msgs_mutex); + + release_ipmi_user(user); } void ipmi_destroy_user(struct ipmi_user *user) { + struct ipmi_smi *intf = user->intf; + + mutex_lock(&intf->users_mutex); _ipmi_destroy_user(user); + mutex_unlock(&intf->users_mutex); - kref_put(&user->refcount, free_user); + kref_put(&user->refcount, free_ipmi_user); } EXPORT_SYMBOL(ipmi_destroy_user); @@ -1411,9 +1426,9 @@ int ipmi_get_version(struct ipmi_user *user, unsigned char *minor) { struct ipmi_device_id id; - int rv, index; + int rv; - user = acquire_ipmi_user(user, &index); + user = acquire_ipmi_user(user); if (!user) return -ENODEV; @@ -1422,7 +1437,7 @@ int ipmi_get_version(struct ipmi_user *user, *major = ipmi_version_major(&id); *minor = ipmi_version_minor(&id); } - release_ipmi_user(user, index); + release_ipmi_user(user); return rv; } @@ -1432,9 +1447,9 @@ int ipmi_set_my_address(struct ipmi_user *user, unsigned int channel, unsigned char address) { - int index, rv = 0; + int rv = 0; - user = acquire_ipmi_user(user, &index); + user = acquire_ipmi_user(user); if (!user) return -ENODEV; @@ -1444,7 +1459,7 @@ int ipmi_set_my_address(struct ipmi_user *user, channel = array_index_nospec(channel, IPMI_MAX_CHANNELS); user->intf->addrinfo[channel].address = address; } - release_ipmi_user(user, index); + release_ipmi_user(user); return rv; } @@ -1454,9 +1469,9 @@ int ipmi_get_my_address(struct ipmi_user *user, unsigned int channel, unsigned char *address) { - int index, rv = 0; + int rv = 0; - user = acquire_ipmi_user(user, &index); + user = acquire_ipmi_user(user); if (!user) return -ENODEV; @@ -1466,7 +1481,7 @@ int ipmi_get_my_address(struct ipmi_user *user, channel = array_index_nospec(channel, IPMI_MAX_CHANNELS); *address = user->intf->addrinfo[channel].address; } - release_ipmi_user(user, index); + release_ipmi_user(user); return rv; } @@ -1476,9 +1491,9 @@ int ipmi_set_my_LUN(struct ipmi_user *user, unsigned int channel, unsigned char LUN) { - int index, rv = 0; + int rv = 0; - user = acquire_ipmi_user(user, &index); + user = acquire_ipmi_user(user); if (!user) return -ENODEV; @@ -1488,7 +1503,7 @@ int ipmi_set_my_LUN(struct ipmi_user *user, channel = array_index_nospec(channel, IPMI_MAX_CHANNELS); user->intf->addrinfo[channel].lun = LUN & 0x3; } - release_ipmi_user(user, index); + release_ipmi_user(user); return rv; } @@ -1498,9 +1513,9 @@ int ipmi_get_my_LUN(struct ipmi_user *user, unsigned int channel, unsigned char *address) { - int index, rv = 0; + int rv = 0; - user = acquire_ipmi_user(user, &index); + user = acquire_ipmi_user(user); if (!user) return -ENODEV; @@ -1510,7 +1525,7 @@ int ipmi_get_my_LUN(struct ipmi_user *user, channel = array_index_nospec(channel, IPMI_MAX_CHANNELS); *address = user->intf->addrinfo[channel].lun; } - release_ipmi_user(user, index); + release_ipmi_user(user); return rv; } @@ -1518,17 +1533,17 @@ EXPORT_SYMBOL(ipmi_get_my_LUN); int ipmi_get_maintenance_mode(struct ipmi_user *user) { - int mode, index; + int mode; unsigned long flags; - user = acquire_ipmi_user(user, &index); + user = acquire_ipmi_user(user); if (!user) return -ENODEV; spin_lock_irqsave(&user->intf->maintenance_mode_lock, flags); mode = user->intf->maintenance_mode; spin_unlock_irqrestore(&user->intf->maintenance_mode_lock, flags); - release_ipmi_user(user, index); + release_ipmi_user(user); return mode; } @@ -1537,17 +1552,24 @@ EXPORT_SYMBOL(ipmi_get_maintenance_mode); static void maintenance_mode_update(struct ipmi_smi *intf) { if (intf->handlers->set_maintenance_mode) + /* + * Lower level drivers only care about firmware mode + * as it affects their timing. They don't care about + * reset, which disables all commands for a while. + */ intf->handlers->set_maintenance_mode( - intf->send_info, intf->maintenance_mode_enable); + intf->send_info, + (intf->maintenance_mode_state == + IPMI_MAINTENANCE_MODE_STATE_FIRMWARE)); } int ipmi_set_maintenance_mode(struct ipmi_user *user, int mode) { - int rv = 0, index; + int rv = 0; unsigned long flags; struct ipmi_smi *intf = user->intf; - user = acquire_ipmi_user(user, &index); + user = acquire_ipmi_user(user); if (!user) return -ENODEV; @@ -1555,16 +1577,17 @@ int ipmi_set_maintenance_mode(struct ipmi_user *user, int mode) if (intf->maintenance_mode != mode) { switch (mode) { case IPMI_MAINTENANCE_MODE_AUTO: - intf->maintenance_mode_enable - = (intf->auto_maintenance_timeout > 0); + /* Just leave it alone. */ break; case IPMI_MAINTENANCE_MODE_OFF: - intf->maintenance_mode_enable = false; + intf->maintenance_mode_state = + IPMI_MAINTENANCE_MODE_STATE_OFF; break; case IPMI_MAINTENANCE_MODE_ON: - intf->maintenance_mode_enable = true; + intf->maintenance_mode_state = + IPMI_MAINTENANCE_MODE_STATE_FIRMWARE; break; default: @@ -1577,7 +1600,7 @@ int ipmi_set_maintenance_mode(struct ipmi_user *user, int mode) } out_unlock: spin_unlock_irqrestore(&intf->maintenance_mode_lock, flags); - release_ipmi_user(user, index); + release_ipmi_user(user); return rv; } @@ -1585,19 +1608,15 @@ EXPORT_SYMBOL(ipmi_set_maintenance_mode); int ipmi_set_gets_events(struct ipmi_user *user, bool val) { - unsigned long flags; struct ipmi_smi *intf = user->intf; struct ipmi_recv_msg *msg, *msg2; - struct list_head msgs; - int index; + LIST_HEAD(msgs); - user = acquire_ipmi_user(user, &index); + user = acquire_ipmi_user(user); if (!user) return -ENODEV; - INIT_LIST_HEAD(&msgs); - - spin_lock_irqsave(&intf->events_lock, flags); + mutex_lock(&intf->events_mutex); if (user->gets_events == val) goto out; @@ -1610,13 +1629,6 @@ int ipmi_set_gets_events(struct ipmi_user *user, bool val) atomic_dec(&intf->event_waiters); } - if (intf->delivering_events) - /* - * Another thread is delivering events for this, so - * let it handle any new events. - */ - goto out; - /* Deliver any queued events. */ while (user->gets_events && !list_empty(&intf->waiting_events)) { list_for_each_entry_safe(msg, msg2, &intf->waiting_events, link) @@ -1627,22 +1639,15 @@ int ipmi_set_gets_events(struct ipmi_user *user, bool val) intf->event_msg_printed = 0; } - intf->delivering_events = 1; - spin_unlock_irqrestore(&intf->events_lock, flags); - list_for_each_entry_safe(msg, msg2, &msgs, link) { - msg->user = user; - kref_get(&user->refcount); + ipmi_set_recv_msg_user(msg, user); deliver_local_response(intf, msg); } - - spin_lock_irqsave(&intf->events_lock, flags); - intf->delivering_events = 0; } out: - spin_unlock_irqrestore(&intf->events_lock, flags); - release_ipmi_user(user, index); + mutex_unlock(&intf->events_mutex); + release_ipmi_user(user); return 0; } @@ -1687,13 +1692,13 @@ int ipmi_register_for_cmd(struct ipmi_user *user, { struct ipmi_smi *intf = user->intf; struct cmd_rcvr *rcvr; - int rv = 0, index; + int rv = 0; - user = acquire_ipmi_user(user, &index); + user = acquire_ipmi_user(user); if (!user) return -ENODEV; - rcvr = kmalloc(sizeof(*rcvr), GFP_KERNEL); + rcvr = kmalloc_obj(*rcvr); if (!rcvr) { rv = -ENOMEM; goto out_release; @@ -1719,7 +1724,7 @@ out_unlock: if (rv) kfree(rcvr); out_release: - release_ipmi_user(user, index); + release_ipmi_user(user); return rv; } @@ -1733,9 +1738,9 @@ int ipmi_unregister_for_cmd(struct ipmi_user *user, struct ipmi_smi *intf = user->intf; struct cmd_rcvr *rcvr; struct cmd_rcvr *rcvrs = NULL; - int i, rv = -ENOENT, index; + int i, rv = -ENOENT; - user = acquire_ipmi_user(user, &index); + user = acquire_ipmi_user(user); if (!user) return -ENODEV; @@ -1758,7 +1763,7 @@ int ipmi_unregister_for_cmd(struct ipmi_user *user, } mutex_unlock(&intf->cmd_rcvrs_mutex); synchronize_rcu(); - release_ipmi_user(user, index); + release_ipmi_user(user); while (rcvrs) { smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_COMMANDS); rcvr = rcvrs; @@ -1878,22 +1883,32 @@ static struct ipmi_smi_msg *smi_add_send_msg(struct ipmi_smi *intf, return smi_msg; } -static void smi_send(struct ipmi_smi *intf, +static int smi_send(struct ipmi_smi *intf, const struct ipmi_smi_handlers *handlers, struct ipmi_smi_msg *smi_msg, int priority) { - int run_to_completion = intf->run_to_completion; + int run_to_completion = READ_ONCE(intf->run_to_completion); unsigned long flags = 0; + int rv = 0; - if (!run_to_completion) - spin_lock_irqsave(&intf->xmit_msgs_lock, flags); + ipmi_lock_xmit_msgs(intf, run_to_completion, &flags); smi_msg = smi_add_send_msg(intf, smi_msg, priority); + ipmi_unlock_xmit_msgs(intf, run_to_completion, &flags); - if (!run_to_completion) - spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags); - - if (smi_msg) - handlers->sender(intf->send_info, smi_msg); + if (smi_msg) { + rv = handlers->sender(intf->send_info, smi_msg); + if (rv) { + ipmi_lock_xmit_msgs(intf, run_to_completion, &flags); + intf->curr_msg = NULL; + ipmi_unlock_xmit_msgs(intf, run_to_completion, &flags); + /* + * Something may have been added to the transmit + * queue, so schedule a check for that. + */ + queue_work(system_wq, &intf->smi_work); + } + } + return rv; } static bool is_maintenance_mode_cmd(struct kernel_ipmi_msg *msg) @@ -1941,14 +1956,20 @@ static int i_ipmi_req_sysintf(struct ipmi_smi *intf, if (is_maintenance_mode_cmd(msg)) { unsigned long flags; + int newst; + + if (msg->netfn == IPMI_NETFN_FIRMWARE_REQUEST) + newst = IPMI_MAINTENANCE_MODE_STATE_FIRMWARE; + else + newst = IPMI_MAINTENANCE_MODE_STATE_RESET; spin_lock_irqsave(&intf->maintenance_mode_lock, flags); - intf->auto_maintenance_timeout - = maintenance_mode_timeout_ms; + intf->auto_maintenance_timeout = maintenance_mode_timeout_ms; if (!intf->maintenance_mode - && !intf->maintenance_mode_enable) { - intf->maintenance_mode_enable = true; + && intf->maintenance_mode_state < newst) { + intf->maintenance_mode_state = newst; maintenance_mode_update(intf); + mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); } spin_unlock_irqrestore(&intf->maintenance_mode_lock, flags); @@ -1962,7 +1983,7 @@ static int i_ipmi_req_sysintf(struct ipmi_smi *intf, smi_msg->data[0] = (msg->netfn << 2) | (smi_addr->lun & 0x3); smi_msg->data[1] = msg->cmd; smi_msg->msgid = msgid; - smi_msg->user_data = recv_msg; + smi_msg->recv_msg = recv_msg; if (msg->data_len > 0) memcpy(&smi_msg->data[2], msg->data, msg->data_len); smi_msg->data_size = msg->data_len + 2; @@ -2043,12 +2064,9 @@ static int i_ipmi_req_ipmb(struct ipmi_smi *intf, * Save the receive message so we can use it * to deliver the response. */ - smi_msg->user_data = recv_msg; + smi_msg->recv_msg = recv_msg; } else { - /* It's a command, so get a sequence for it. */ - unsigned long flags; - - spin_lock_irqsave(&intf->seq_lock, flags); + mutex_lock(&intf->seq_lock); if (is_maintenance_mode_cmd(msg)) intf->ipmb_maintenance_mode_timeout = @@ -2106,7 +2124,7 @@ static int i_ipmi_req_ipmb(struct ipmi_smi *intf, * to be correct. */ out_err: - spin_unlock_irqrestore(&intf->seq_lock, flags); + mutex_unlock(&intf->seq_lock); } return rv; @@ -2159,7 +2177,7 @@ static int i_ipmi_req_ipmb_direct(struct ipmi_smi *intf, memcpy(smi_msg->data + 4, msg->data, msg->data_len); smi_msg->data_size = msg->data_len + 4; - smi_msg->user_data = recv_msg; + smi_msg->recv_msg = recv_msg; return 0; } @@ -2222,12 +2240,9 @@ static int i_ipmi_req_lan(struct ipmi_smi *intf, * Save the receive message so we can use it * to deliver the response. */ - smi_msg->user_data = recv_msg; + smi_msg->recv_msg = recv_msg; } else { - /* It's a command, so get a sequence for it. */ - unsigned long flags; - - spin_lock_irqsave(&intf->seq_lock, flags); + mutex_lock(&intf->seq_lock); /* * Create a sequence number with a 1 second @@ -2276,7 +2291,7 @@ static int i_ipmi_req_lan(struct ipmi_smi *intf, * to be correct. */ out_err: - spin_unlock_irqrestore(&intf->seq_lock, flags); + mutex_unlock(&intf->seq_lock); } return rv; @@ -2304,24 +2319,22 @@ static int i_ipmi_request(struct ipmi_user *user, { struct ipmi_smi_msg *smi_msg; struct ipmi_recv_msg *recv_msg; + int run_to_completion = READ_ONCE(intf->run_to_completion); int rv = 0; + bool in_seq_table = false; - if (user) { - if (atomic_add_return(1, &user->nr_msgs) > max_msgs_per_user) { - /* Decrement will happen at the end of the routine. */ - rv = -EBUSY; - goto out; - } - } - - if (supplied_recv) + if (supplied_recv) { recv_msg = supplied_recv; - else { - recv_msg = ipmi_alloc_recv_msg(); - if (recv_msg == NULL) { - rv = -ENOMEM; - goto out; + recv_msg->user = user; + if (user) { + atomic_inc(&user->nr_msgs); + /* The put happens when the message is freed. */ + kref_get(&user->refcount); } + } else { + recv_msg = ipmi_alloc_recv_msg(user); + if (IS_ERR(recv_msg)) + return PTR_ERR(recv_msg); } recv_msg->user_msg_data = user_msg_data; @@ -2332,21 +2345,26 @@ static int i_ipmi_request(struct ipmi_user *user, if (smi_msg == NULL) { if (!supplied_recv) ipmi_free_recv_msg(recv_msg); - rv = -ENOMEM; - goto out; + else if (recv_msg->user) { + atomic_dec(&recv_msg->user->nr_msgs); + kref_put(&recv_msg->user->refcount, free_ipmi_user); + } + return -ENOMEM; } } - rcu_read_lock(); + if (!run_to_completion) + mutex_lock(&intf->users_mutex); + if (intf->maintenance_mode_state == IPMI_MAINTENANCE_MODE_STATE_RESET) { + /* No messages while the BMC is in reset. */ + rv = -EBUSY; + goto out_err; + } if (intf->in_shutdown) { rv = -ENODEV; goto out_err; } - recv_msg->user = user; - if (user) - /* The put happens when the message is freed. */ - kref_get(&user->refcount); recv_msg->msgid = msgid; /* * Store the message to send in the receive message so timeout @@ -2361,33 +2379,54 @@ static int i_ipmi_request(struct ipmi_user *user, rv = i_ipmi_req_ipmb(intf, addr, msgid, msg, smi_msg, recv_msg, source_address, source_lun, retries, retry_time_ms); + in_seq_table = true; } else if (is_ipmb_direct_addr(addr)) { rv = i_ipmi_req_ipmb_direct(intf, addr, msgid, msg, smi_msg, recv_msg, source_lun); } else if (is_lan_addr(addr)) { rv = i_ipmi_req_lan(intf, addr, msgid, msg, smi_msg, recv_msg, source_lun, retries, retry_time_ms); + in_seq_table = true; } else { - /* Unknown address type. */ + /* Unknown address type. */ ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; } - if (rv) { -out_err: - ipmi_free_smi_msg(smi_msg); - ipmi_free_recv_msg(recv_msg); - } else { + if (!rv) { dev_dbg(intf->si_dev, "Send: %*ph\n", smi_msg->data_size, smi_msg->data); - smi_send(intf, intf->handlers, smi_msg, priority); + rv = smi_send(intf, intf->handlers, smi_msg, priority); + if (rv != IPMI_CC_NO_ERROR) + /* smi_send() returns an IPMI err, return a Linux one. */ + rv = -EIO; + if (rv && in_seq_table) { + /* + * If it's in the sequence table, it will be + * retried later, so ignore errors. + */ + rv = 0; + /* But we need to fix the timeout. */ + intf_start_seq_timer(intf, smi_msg->msgid); + ipmi_free_smi_msg(smi_msg); + smi_msg = NULL; + } } - rcu_read_unlock(); +out_err: + if (!run_to_completion) + mutex_unlock(&intf->users_mutex); -out: - if (rv && user) - atomic_dec(&user->nr_msgs); + if (rv) { + if (!supplied_smi) + ipmi_free_smi_msg(smi_msg); + if (!supplied_recv) + ipmi_free_recv_msg(recv_msg); + else if (recv_msg->user) { + atomic_dec(&recv_msg->user->nr_msgs); + kref_put(&recv_msg->user->refcount, free_ipmi_user); + } + } return rv; } @@ -2414,12 +2453,12 @@ int ipmi_request_settime(struct ipmi_user *user, unsigned int retry_time_ms) { unsigned char saddr = 0, lun = 0; - int rv, index; + int rv; if (!user) return -EINVAL; - user = acquire_ipmi_user(user, &index); + user = acquire_ipmi_user(user); if (!user) return -ENODEV; @@ -2438,7 +2477,7 @@ int ipmi_request_settime(struct ipmi_user *user, retries, retry_time_ms); - release_ipmi_user(user, index); + release_ipmi_user(user); return rv; } EXPORT_SYMBOL(ipmi_request_settime); @@ -2453,12 +2492,12 @@ int ipmi_request_supply_msgs(struct ipmi_user *user, int priority) { unsigned char saddr = 0, lun = 0; - int rv, index; + int rv; if (!user) return -EINVAL; - user = acquire_ipmi_user(user, &index); + user = acquire_ipmi_user(user); if (!user) return -ENODEV; @@ -2477,7 +2516,7 @@ int ipmi_request_supply_msgs(struct ipmi_user *user, lun, -1, 0); - release_ipmi_user(user, index); + release_ipmi_user(user); return rv; } EXPORT_SYMBOL(ipmi_request_supply_msgs); @@ -2638,6 +2677,12 @@ retry_bmc_lock: (bmc->dyn_id_set && time_is_after_jiffies(bmc->dyn_id_expiry))) goto out_noprocessing; + /* Don't allow sysfs access when in maintenance mode. */ + if (intf->maintenance_mode_state) { + rv = -EBUSY; + goto out_noprocessing; + } + prev_guid_set = bmc->dyn_guid_set; __get_guid(intf); @@ -2673,7 +2718,7 @@ retry_bmc_lock: if (__ipmi_bmc_register(intf, &id, guid_set, &guid, intf_num)) need_waiter(intf); /* Retry later on an error. */ else - __scan_channels(intf, &id); + __scan_channels(intf, &id, false); if (!intf_set) { @@ -2693,7 +2738,7 @@ retry_bmc_lock: goto out_noprocessing; } else if (memcmp(&bmc->fetch_id, &bmc->id, sizeof(bmc->id))) /* Version info changes, scan the channels again. */ - __scan_channels(intf, &bmc->fetch_id); + __scan_channels(intf, &bmc->fetch_id, true); bmc->dyn_id_expiry = jiffies + IPMI_DYN_DEV_ID_EXPIRY; @@ -3064,7 +3109,7 @@ cleanup_bmc_device(struct kref *ref) * with removing the device attributes while reading a device * attribute. */ - queue_work(remove_work_wq, &bmc->remove_work); + queue_work(bmc_remove_work_wq, &bmc->remove_work); } /* @@ -3150,7 +3195,7 @@ static int __ipmi_bmc_register(struct ipmi_smi *intf, bmc->id.product_id, bmc->id.device_id); } else { - bmc = kzalloc(sizeof(*bmc), GFP_KERNEL); + bmc = kzalloc_obj(*bmc); if (!bmc) { rv = -ENOMEM; goto out; @@ -3422,8 +3467,6 @@ channel_handler(struct ipmi_smi *intf, struct ipmi_recv_msg *msg) intf->channels_ready = true; wake_up(&intf->waitq); } else { - intf->channel_list = intf->wchannels + set; - intf->channels_ready = true; rv = send_channel_info_cmd(intf, intf->curr_channel); } @@ -3445,10 +3488,21 @@ channel_handler(struct ipmi_smi *intf, struct ipmi_recv_msg *msg) /* * Must be holding intf->bmc_reg_mutex to call this. */ -static int __scan_channels(struct ipmi_smi *intf, struct ipmi_device_id *id) +static int __scan_channels(struct ipmi_smi *intf, + struct ipmi_device_id *id, + bool rescan) { int rv; + if (rescan) { + /* Clear channels_ready to force channels rescan. */ + intf->channels_ready = false; + } + + /* Skip channel scan if channels are already marked ready */ + if (intf->channels_ready) + return 0; + if (ipmi_version_major(id) > 1 || (ipmi_version_major(id) == 1 && ipmi_version_minor(id) >= 5)) { @@ -3520,20 +3574,32 @@ static ssize_t nr_msgs_show(struct device *dev, char *buf) { struct ipmi_smi *intf = container_of(attr, - struct ipmi_smi, nr_msgs_devattr); + struct ipmi_smi, nr_msgs_devattr); struct ipmi_user *user; - int index; unsigned int count = 0; - index = srcu_read_lock(&intf->users_srcu); - list_for_each_entry_rcu(user, &intf->users, link) + mutex_lock(&intf->users_mutex); + list_for_each_entry(user, &intf->users, link) count += atomic_read(&user->nr_msgs); - srcu_read_unlock(&intf->users_srcu, index); + mutex_unlock(&intf->users_mutex); return sysfs_emit(buf, "%u\n", count); } static DEVICE_ATTR_RO(nr_msgs); +static ssize_t maintenance_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ipmi_smi *intf = container_of(attr, + struct ipmi_smi, + maintenance_mode_devattr); + + return sysfs_emit(buf, "%u %d\n", intf->maintenance_mode_state, + intf->auto_maintenance_timeout); +} +static DEVICE_ATTR_RO(maintenance_mode); + static void redo_bmc_reg(struct work_struct *work) { struct ipmi_smi *intf = container_of(work, struct ipmi_smi, @@ -3565,16 +3631,10 @@ int ipmi_add_smi(struct module *owner, if (rv) return rv; - intf = kzalloc(sizeof(*intf), GFP_KERNEL); + intf = kzalloc_obj(*intf); if (!intf) return -ENOMEM; - rv = init_srcu_struct(&intf->users_srcu); - if (rv) { - kfree(intf); - return rv; - } - intf->owner = owner; intf->bmc = &intf->tmp_bmc; INIT_LIST_HEAD(&intf->bmc->intfs); @@ -3591,11 +3651,14 @@ int ipmi_add_smi(struct module *owner, } if (slave_addr != 0) intf->addrinfo[0].address = slave_addr; + INIT_LIST_HEAD(&intf->user_msgs); + mutex_init(&intf->user_msgs_mutex); INIT_LIST_HEAD(&intf->users); + mutex_init(&intf->users_mutex); atomic_set(&intf->nr_users, 0); intf->handlers = handlers; intf->send_info = send_info; - spin_lock_init(&intf->seq_lock); + mutex_init(&intf->seq_lock); for (j = 0; j < IPMI_IPMB_NUM_SEQ; j++) { intf->seq_table[j].inuse = 0; intf->seq_table[j].seqid = 0; @@ -3603,12 +3666,12 @@ int ipmi_add_smi(struct module *owner, intf->curr_seq = 0; spin_lock_init(&intf->waiting_rcv_msgs_lock); INIT_LIST_HEAD(&intf->waiting_rcv_msgs); - INIT_WORK(&intf->recv_work, smi_recv_work); + INIT_WORK(&intf->smi_work, smi_work); atomic_set(&intf->watchdog_pretimeouts_to_deliver, 0); spin_lock_init(&intf->xmit_msgs_lock); INIT_LIST_HEAD(&intf->xmit_msgs); INIT_LIST_HEAD(&intf->hp_xmit_msgs); - spin_lock_init(&intf->events_lock); + mutex_init(&intf->events_mutex); spin_lock_init(&intf->watch_lock); atomic_set(&intf->event_waiters, 0); intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME; @@ -3621,12 +3684,16 @@ int ipmi_add_smi(struct module *owner, for (i = 0; i < IPMI_NUM_STATS; i++) atomic_set(&intf->stats[i], 0); + /* + * Grab the watchers mutex so we can deliver the new interface + * without races. + */ + mutex_lock(&smi_watchers_mutex); mutex_lock(&ipmi_interfaces_mutex); /* Look for a hole in the numbers. */ i = 0; link = &ipmi_interfaces; - list_for_each_entry_rcu(tintf, &ipmi_interfaces, link, - ipmi_interfaces_mutex_held()) { + list_for_each_entry(tintf, &ipmi_interfaces, link) { if (tintf->intf_num != i) { link = &tintf->link; break; @@ -3635,9 +3702,9 @@ int ipmi_add_smi(struct module *owner, } /* Add the new interface in numeric order. */ if (i == 0) - list_add_rcu(&intf->link, &ipmi_interfaces); + list_add(&intf->link, &ipmi_interfaces); else - list_add_tail_rcu(&intf->link, link); + list_add_tail(&intf->link, link); rv = handlers->start_processing(send_info, intf); if (rv) @@ -3650,7 +3717,7 @@ int ipmi_add_smi(struct module *owner, } mutex_lock(&intf->bmc_reg_mutex); - rv = __scan_channels(intf, &id); + rv = __scan_channels(intf, &id, false); mutex_unlock(&intf->bmc_reg_mutex); if (rv) goto out_err_bmc_reg; @@ -3669,18 +3736,22 @@ int ipmi_add_smi(struct module *owner, goto out_err_bmc_reg; } - /* - * Keep memory order straight for RCU readers. Make - * sure everything else is committed to memory before - * setting intf_num to mark the interface valid. - */ - smp_wmb(); + intf->maintenance_mode_devattr = dev_attr_maintenance_mode; + sysfs_attr_init(&intf->maintenance_mode_devattr.attr); + rv = device_create_file(intf->si_dev, &intf->maintenance_mode_devattr); + if (rv) { + device_remove_file(intf->si_dev, &intf->nr_users_devattr); + goto out_err_bmc_reg; + } + intf->intf_num = i; mutex_unlock(&ipmi_interfaces_mutex); /* After this point the interface is legal to use. */ call_smi_watchers(i, intf->si_dev); + mutex_unlock(&smi_watchers_mutex); + return 0; out_err_bmc_reg: @@ -3689,10 +3760,9 @@ int ipmi_add_smi(struct module *owner, if (intf->handlers->shutdown) intf->handlers->shutdown(intf->send_info); out_err: - list_del_rcu(&intf->link); + list_del(&intf->link); mutex_unlock(&ipmi_interfaces_mutex); - synchronize_srcu(&ipmi_interfaces_srcu); - cleanup_srcu_struct(&intf->users_srcu); + mutex_unlock(&smi_watchers_mutex); kref_put(&intf->refcount, intf_free); return rv; @@ -3721,10 +3791,9 @@ static void cleanup_smi_msgs(struct ipmi_smi *intf) struct seq_table *ent; struct ipmi_smi_msg *msg; struct list_head *entry; - struct list_head tmplist; + LIST_HEAD(tmplist); /* Clear out our transmit queues and hold the messages. */ - INIT_LIST_HEAD(&tmplist); list_splice_tail(&intf->hp_xmit_msgs, &tmplist); list_splice_tail(&intf->xmit_msgs, &tmplist); @@ -3758,20 +3827,30 @@ static void cleanup_smi_msgs(struct ipmi_smi *intf) void ipmi_unregister_smi(struct ipmi_smi *intf) { struct ipmi_smi_watcher *w; - int intf_num, index; + int intf_num; if (!intf) return; + intf_num = intf->intf_num; mutex_lock(&ipmi_interfaces_mutex); + cancel_work_sync(&intf->smi_work); + /* smi_work() can no longer be in progress after this. */ + intf->intf_num = -1; intf->in_shutdown = true; - list_del_rcu(&intf->link); + list_del(&intf->link); mutex_unlock(&ipmi_interfaces_mutex); - synchronize_srcu(&ipmi_interfaces_srcu); - /* At this point no users can be added to the interface. */ + /* + * At this point no users can be added to the interface and no + * new messages can be sent. + */ + + if (intf->handlers->shutdown) + intf->handlers->shutdown(intf->send_info); + device_remove_file(intf->si_dev, &intf->maintenance_mode_devattr); device_remove_file(intf->si_dev, &intf->nr_msgs_devattr); device_remove_file(intf->si_dev, &intf->nr_users_devattr); @@ -3784,24 +3863,19 @@ void ipmi_unregister_smi(struct ipmi_smi *intf) w->smi_gone(intf_num); mutex_unlock(&smi_watchers_mutex); - index = srcu_read_lock(&intf->users_srcu); + mutex_lock(&intf->users_mutex); while (!list_empty(&intf->users)) { - struct ipmi_user *user = - container_of(list_next_rcu(&intf->users), - struct ipmi_user, link); + struct ipmi_user *user = list_first_entry(&intf->users, + struct ipmi_user, link); _ipmi_destroy_user(user); } - srcu_read_unlock(&intf->users_srcu, index); - - if (intf->handlers->shutdown) - intf->handlers->shutdown(intf->send_info); + mutex_unlock(&intf->users_mutex); cleanup_smi_msgs(intf); ipmi_bmc_unregister(intf); - cleanup_srcu_struct(&intf->users_srcu); kref_put(&intf->refcount, intf_free); } EXPORT_SYMBOL(ipmi_unregister_smi); @@ -3879,7 +3953,7 @@ static int handle_ipmb_get_msg_cmd(struct ipmi_smi *intf, unsigned char chan; struct ipmi_user *user = NULL; struct ipmi_ipmb_addr *ipmb_addr; - struct ipmi_recv_msg *recv_msg; + struct ipmi_recv_msg *recv_msg = NULL; if (msg->rsp_size < 10) { /* Message not big enough, just ignore it. */ @@ -3900,9 +3974,8 @@ static int handle_ipmb_get_msg_cmd(struct ipmi_smi *intf, rcvr = find_cmd_rcvr(intf, netfn, cmd, chan); if (rcvr) { user = rcvr->user; - kref_get(&user->refcount); - } else - user = NULL; + recv_msg = ipmi_alloc_recv_msg(user); + } rcu_read_unlock(); if (user == NULL) { @@ -3926,58 +3999,47 @@ static int handle_ipmb_get_msg_cmd(struct ipmi_smi *intf, dev_dbg(intf->si_dev, "Invalid command: %*ph\n", msg->data_size, msg->data); - rcu_read_lock(); - if (!intf->in_shutdown) { - smi_send(intf, intf->handlers, msg, 0); + if (smi_send(intf, intf->handlers, msg, 0) == IPMI_CC_NO_ERROR) /* - * We used the message, so return the value - * that causes it to not be freed or - * queued. + * We used the message, so return the value that + * causes it to not be freed or queued. */ rv = -1; - } - rcu_read_unlock(); - } else { - recv_msg = ipmi_alloc_recv_msg(); - if (!recv_msg) { - /* - * We couldn't allocate memory for the - * message, so requeue it for handling - * later. - */ - rv = 1; - kref_put(&user->refcount, free_user); - } else { - /* Extract the source address from the data. */ - ipmb_addr = (struct ipmi_ipmb_addr *) &recv_msg->addr; - ipmb_addr->addr_type = IPMI_IPMB_ADDR_TYPE; - ipmb_addr->slave_addr = msg->rsp[6]; - ipmb_addr->lun = msg->rsp[7] & 3; - ipmb_addr->channel = msg->rsp[3] & 0xf; + } else if (!IS_ERR(recv_msg)) { + /* Extract the source address from the data. */ + ipmb_addr = (struct ipmi_ipmb_addr *) &recv_msg->addr; + ipmb_addr->addr_type = IPMI_IPMB_ADDR_TYPE; + ipmb_addr->slave_addr = msg->rsp[6]; + ipmb_addr->lun = msg->rsp[7] & 3; + ipmb_addr->channel = msg->rsp[3] & 0xf; - /* - * Extract the rest of the message information - * from the IPMB header. - */ - recv_msg->user = user; - recv_msg->recv_type = IPMI_CMD_RECV_TYPE; - recv_msg->msgid = msg->rsp[7] >> 2; - recv_msg->msg.netfn = msg->rsp[4] >> 2; - recv_msg->msg.cmd = msg->rsp[8]; - recv_msg->msg.data = recv_msg->msg_data; + /* + * Extract the rest of the message information + * from the IPMB header. + */ + recv_msg->recv_type = IPMI_CMD_RECV_TYPE; + recv_msg->msgid = msg->rsp[7] >> 2; + recv_msg->msg.netfn = msg->rsp[4] >> 2; + recv_msg->msg.cmd = msg->rsp[8]; + recv_msg->msg.data = recv_msg->msg_data; - /* - * We chop off 10, not 9 bytes because the checksum - * at the end also needs to be removed. - */ - recv_msg->msg.data_len = msg->rsp_size - 10; - memcpy(recv_msg->msg_data, &msg->rsp[9], - msg->rsp_size - 10); - if (deliver_response(intf, recv_msg)) - ipmi_inc_stat(intf, unhandled_commands); - else - ipmi_inc_stat(intf, handled_commands); - } + /* + * We chop off 10, not 9 bytes because the checksum + * at the end also needs to be removed. + */ + recv_msg->msg.data_len = msg->rsp_size - 10; + memcpy(recv_msg->msg_data, &msg->rsp[9], + msg->rsp_size - 10); + if (deliver_response(intf, recv_msg)) + ipmi_inc_stat(intf, unhandled_commands); + else + ipmi_inc_stat(intf, handled_commands); + } else { + /* + * We couldn't allocate memory for the message, so + * requeue it for handling later. + */ + rv = 1; } return rv; @@ -3990,7 +4052,7 @@ static int handle_ipmb_direct_rcv_cmd(struct ipmi_smi *intf, int rv = 0; struct ipmi_user *user = NULL; struct ipmi_ipmb_direct_addr *daddr; - struct ipmi_recv_msg *recv_msg; + struct ipmi_recv_msg *recv_msg = NULL; unsigned char netfn = msg->rsp[0] >> 2; unsigned char cmd = msg->rsp[3]; @@ -3999,9 +4061,8 @@ static int handle_ipmb_direct_rcv_cmd(struct ipmi_smi *intf, rcvr = find_cmd_rcvr(intf, netfn, cmd, 0); if (rcvr) { user = rcvr->user; - kref_get(&user->refcount); - } else - user = NULL; + recv_msg = ipmi_alloc_recv_msg(user); + } rcu_read_unlock(); if (user == NULL) { @@ -4017,55 +4078,44 @@ static int handle_ipmb_direct_rcv_cmd(struct ipmi_smi *intf, msg->data[4] = IPMI_INVALID_CMD_COMPLETION_CODE; msg->data_size = 5; - rcu_read_lock(); - if (!intf->in_shutdown) { - smi_send(intf, intf->handlers, msg, 0); + if (smi_send(intf, intf->handlers, msg, 0) == IPMI_CC_NO_ERROR) /* - * We used the message, so return the value - * that causes it to not be freed or - * queued. + * We used the message, so return the value that + * causes it to not be freed or queued. */ rv = -1; - } - rcu_read_unlock(); - } else { - recv_msg = ipmi_alloc_recv_msg(); - if (!recv_msg) { - /* - * We couldn't allocate memory for the - * message, so requeue it for handling - * later. - */ - rv = 1; - kref_put(&user->refcount, free_user); - } else { - /* Extract the source address from the data. */ - daddr = (struct ipmi_ipmb_direct_addr *)&recv_msg->addr; - daddr->addr_type = IPMI_IPMB_DIRECT_ADDR_TYPE; - daddr->channel = 0; - daddr->slave_addr = msg->rsp[1]; - daddr->rs_lun = msg->rsp[0] & 3; - daddr->rq_lun = msg->rsp[2] & 3; + } else if (!IS_ERR(recv_msg)) { + /* Extract the source address from the data. */ + daddr = (struct ipmi_ipmb_direct_addr *)&recv_msg->addr; + daddr->addr_type = IPMI_IPMB_DIRECT_ADDR_TYPE; + daddr->channel = 0; + daddr->slave_addr = msg->rsp[1]; + daddr->rs_lun = msg->rsp[0] & 3; + daddr->rq_lun = msg->rsp[2] & 3; - /* - * Extract the rest of the message information - * from the IPMB header. - */ - recv_msg->user = user; - recv_msg->recv_type = IPMI_CMD_RECV_TYPE; - recv_msg->msgid = (msg->rsp[2] >> 2); - recv_msg->msg.netfn = msg->rsp[0] >> 2; - recv_msg->msg.cmd = msg->rsp[3]; - recv_msg->msg.data = recv_msg->msg_data; - - recv_msg->msg.data_len = msg->rsp_size - 4; - memcpy(recv_msg->msg_data, msg->rsp + 4, - msg->rsp_size - 4); - if (deliver_response(intf, recv_msg)) - ipmi_inc_stat(intf, unhandled_commands); - else - ipmi_inc_stat(intf, handled_commands); - } + /* + * Extract the rest of the message information + * from the IPMB header. + */ + recv_msg->recv_type = IPMI_CMD_RECV_TYPE; + recv_msg->msgid = (msg->rsp[2] >> 2); + recv_msg->msg.netfn = msg->rsp[0] >> 2; + recv_msg->msg.cmd = msg->rsp[3]; + recv_msg->msg.data = recv_msg->msg_data; + + recv_msg->msg.data_len = msg->rsp_size - 4; + memcpy(recv_msg->msg_data, msg->rsp + 4, + msg->rsp_size - 4); + if (deliver_response(intf, recv_msg)) + ipmi_inc_stat(intf, unhandled_commands); + else + ipmi_inc_stat(intf, handled_commands); + } else { + /* + * We couldn't allocate memory for the message, so + * requeue it for handling later. + */ + rv = 1; } return rv; @@ -4077,7 +4127,7 @@ static int handle_ipmb_direct_rcv_rsp(struct ipmi_smi *intf, struct ipmi_recv_msg *recv_msg; struct ipmi_ipmb_direct_addr *daddr; - recv_msg = msg->user_data; + recv_msg = msg->recv_msg; if (recv_msg == NULL) { dev_warn(intf->si_dev, "IPMI direct message received with no owner. This could be because of a malformed message, or because of a hardware error. Contact your hardware vendor for assistance.\n"); @@ -4173,13 +4223,13 @@ static int handle_lan_get_msg_cmd(struct ipmi_smi *intf, struct ipmi_smi_msg *msg) { struct cmd_rcvr *rcvr; - int rv = 0; + int rv = 0; /* Free by default */ unsigned char netfn; unsigned char cmd; unsigned char chan; struct ipmi_user *user = NULL; struct ipmi_lan_addr *lan_addr; - struct ipmi_recv_msg *recv_msg; + struct ipmi_recv_msg *recv_msg = NULL; if (msg->rsp_size < 12) { /* Message not big enough, just ignore it. */ @@ -4200,63 +4250,76 @@ static int handle_lan_get_msg_cmd(struct ipmi_smi *intf, rcvr = find_cmd_rcvr(intf, netfn, cmd, chan); if (rcvr) { user = rcvr->user; - kref_get(&user->refcount); - } else - user = NULL; + recv_msg = ipmi_alloc_recv_msg(user); + } rcu_read_unlock(); if (user == NULL) { - /* We didn't find a user, just give up. */ + /* We didn't find a user, just give up and return an error. */ ipmi_inc_stat(intf, unhandled_commands); - /* - * Don't do anything with these messages, just allow - * them to be freed. - */ - rv = 0; - } else { - recv_msg = ipmi_alloc_recv_msg(); - if (!recv_msg) { - /* - * We couldn't allocate memory for the - * message, so requeue it for handling later. - */ - rv = 1; - kref_put(&user->refcount, free_user); - } else { - /* Extract the source address from the data. */ - lan_addr = (struct ipmi_lan_addr *) &recv_msg->addr; - lan_addr->addr_type = IPMI_LAN_ADDR_TYPE; - lan_addr->session_handle = msg->rsp[4]; - lan_addr->remote_SWID = msg->rsp[8]; - lan_addr->local_SWID = msg->rsp[5]; - lan_addr->lun = msg->rsp[9] & 3; - lan_addr->channel = msg->rsp[3] & 0xf; - lan_addr->privilege = msg->rsp[3] >> 4; + msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); + msg->data[1] = IPMI_SEND_MSG_CMD; + msg->data[2] = chan; + msg->data[3] = msg->rsp[4]; /* handle */ + msg->data[4] = msg->rsp[8]; /* rsSWID */ + msg->data[5] = ((netfn + 1) << 2) | (msg->rsp[9] & 0x3); + msg->data[6] = ipmb_checksum(&msg->data[3], 3); + msg->data[7] = msg->rsp[5]; /* rqSWID */ + /* rqseq/lun */ + msg->data[8] = (msg->rsp[9] & 0xfc) | (msg->rsp[6] & 0x3); + msg->data[9] = cmd; + msg->data[10] = IPMI_INVALID_CMD_COMPLETION_CODE; + msg->data[11] = ipmb_checksum(&msg->data[7], 4); + msg->data_size = 12; - /* - * Extract the rest of the message information - * from the IPMB header. - */ - recv_msg->user = user; - recv_msg->recv_type = IPMI_CMD_RECV_TYPE; - recv_msg->msgid = msg->rsp[9] >> 2; - recv_msg->msg.netfn = msg->rsp[6] >> 2; - recv_msg->msg.cmd = msg->rsp[10]; - recv_msg->msg.data = recv_msg->msg_data; + dev_dbg(intf->si_dev, "Invalid command: %*ph\n", + msg->data_size, msg->data); + if (smi_send(intf, intf->handlers, msg, 0) == IPMI_CC_NO_ERROR) /* - * We chop off 12, not 11 bytes because the checksum - * at the end also needs to be removed. + * We used the message, so return the value that + * causes it to not be freed or queued. */ - recv_msg->msg.data_len = msg->rsp_size - 12; - memcpy(recv_msg->msg_data, &msg->rsp[11], - msg->rsp_size - 12); - if (deliver_response(intf, recv_msg)) - ipmi_inc_stat(intf, unhandled_commands); - else - ipmi_inc_stat(intf, handled_commands); - } + rv = -1; + } else if (!IS_ERR(recv_msg)) { + /* Extract the source address from the data. */ + lan_addr = (struct ipmi_lan_addr *) &recv_msg->addr; + lan_addr->addr_type = IPMI_LAN_ADDR_TYPE; + lan_addr->session_handle = msg->rsp[4]; + lan_addr->remote_SWID = msg->rsp[8]; + lan_addr->local_SWID = msg->rsp[5]; + lan_addr->lun = msg->rsp[9] & 3; + lan_addr->channel = msg->rsp[3] & 0xf; + lan_addr->privilege = msg->rsp[3] >> 4; + + /* + * Extract the rest of the message information + * from the IPMB header. + */ + recv_msg->recv_type = IPMI_CMD_RECV_TYPE; + recv_msg->msgid = msg->rsp[9] >> 2; + recv_msg->msg.netfn = msg->rsp[6] >> 2; + recv_msg->msg.cmd = msg->rsp[10]; + recv_msg->msg.data = recv_msg->msg_data; + + /* + * We chop off 12, not 11 bytes because the checksum + * at the end also needs to be removed. + */ + recv_msg->msg.data_len = msg->rsp_size - 12; + memcpy(recv_msg->msg_data, &msg->rsp[11], + msg->rsp_size - 12); + if (deliver_response(intf, recv_msg)) + ipmi_inc_stat(intf, unhandled_commands); + else + ipmi_inc_stat(intf, handled_commands); + } else { + /* + * We couldn't allocate memory for the message, so + * requeue it for handling later. + */ + rv = 1; } return rv; @@ -4278,7 +4341,7 @@ static int handle_oem_get_msg_cmd(struct ipmi_smi *intf, unsigned char chan; struct ipmi_user *user = NULL; struct ipmi_system_interface_addr *smi_addr; - struct ipmi_recv_msg *recv_msg; + struct ipmi_recv_msg *recv_msg = NULL; /* * We expect the OEM SW to perform error checking @@ -4307,9 +4370,8 @@ static int handle_oem_get_msg_cmd(struct ipmi_smi *intf, rcvr = find_cmd_rcvr(intf, netfn, cmd, chan); if (rcvr) { user = rcvr->user; - kref_get(&user->refcount); - } else - user = NULL; + recv_msg = ipmi_alloc_recv_msg(user); + } rcu_read_unlock(); if (user == NULL) { @@ -4322,48 +4384,42 @@ static int handle_oem_get_msg_cmd(struct ipmi_smi *intf, */ rv = 0; - } else { - recv_msg = ipmi_alloc_recv_msg(); - if (!recv_msg) { - /* - * We couldn't allocate memory for the - * message, so requeue it for handling - * later. - */ - rv = 1; - kref_put(&user->refcount, free_user); - } else { - /* - * OEM Messages are expected to be delivered via - * the system interface to SMS software. We might - * need to visit this again depending on OEM - * requirements - */ - smi_addr = ((struct ipmi_system_interface_addr *) - &recv_msg->addr); - smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; - smi_addr->channel = IPMI_BMC_CHANNEL; - smi_addr->lun = msg->rsp[0] & 3; - - recv_msg->user = user; - recv_msg->user_msg_data = NULL; - recv_msg->recv_type = IPMI_OEM_RECV_TYPE; - recv_msg->msg.netfn = msg->rsp[0] >> 2; - recv_msg->msg.cmd = msg->rsp[1]; - recv_msg->msg.data = recv_msg->msg_data; + } else if (!IS_ERR(recv_msg)) { + /* + * OEM Messages are expected to be delivered via + * the system interface to SMS software. We might + * need to visit this again depending on OEM + * requirements + */ + smi_addr = ((struct ipmi_system_interface_addr *) + &recv_msg->addr); + smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + smi_addr->channel = IPMI_BMC_CHANNEL; + smi_addr->lun = msg->rsp[0] & 3; + + recv_msg->user_msg_data = NULL; + recv_msg->recv_type = IPMI_OEM_RECV_TYPE; + recv_msg->msg.netfn = msg->rsp[0] >> 2; + recv_msg->msg.cmd = msg->rsp[1]; + recv_msg->msg.data = recv_msg->msg_data; - /* - * The message starts at byte 4 which follows the - * Channel Byte in the "GET MESSAGE" command - */ - recv_msg->msg.data_len = msg->rsp_size - 4; - memcpy(recv_msg->msg_data, &msg->rsp[4], - msg->rsp_size - 4); - if (deliver_response(intf, recv_msg)) - ipmi_inc_stat(intf, unhandled_commands); - else - ipmi_inc_stat(intf, handled_commands); - } + /* + * The message starts at byte 4 which follows the + * Channel Byte in the "GET MESSAGE" command + */ + recv_msg->msg.data_len = msg->rsp_size - 4; + memcpy(recv_msg->msg_data, &msg->rsp[4], + msg->rsp_size - 4); + if (deliver_response(intf, recv_msg)) + ipmi_inc_stat(intf, unhandled_commands); + else + ipmi_inc_stat(intf, handled_commands); + } else { + /* + * We couldn't allocate memory for the message, so + * requeue it for handling later. + */ + rv = 1; } return rv; @@ -4391,10 +4447,9 @@ static int handle_read_event_rsp(struct ipmi_smi *intf, struct ipmi_smi_msg *msg) { struct ipmi_recv_msg *recv_msg, *recv_msg2; - struct list_head msgs; + LIST_HEAD(msgs); struct ipmi_user *user; - int rv = 0, deliver_count = 0, index; - unsigned long flags; + int rv = 0, deliver_count = 0; if (msg->rsp_size < 19) { /* Message is too small to be an IPMB event. */ @@ -4407,9 +4462,7 @@ static int handle_read_event_rsp(struct ipmi_smi *intf, return 0; } - INIT_LIST_HEAD(&msgs); - - spin_lock_irqsave(&intf->events_lock, flags); + mutex_lock(&intf->events_mutex); ipmi_inc_stat(intf, events); @@ -4417,14 +4470,14 @@ static int handle_read_event_rsp(struct ipmi_smi *intf, * Allocate and fill in one message for every user that is * getting events. */ - index = srcu_read_lock(&intf->users_srcu); - list_for_each_entry_rcu(user, &intf->users, link) { + mutex_lock(&intf->users_mutex); + list_for_each_entry(user, &intf->users, link) { if (!user->gets_events) continue; - recv_msg = ipmi_alloc_recv_msg(); - if (!recv_msg) { - rcu_read_unlock(); + recv_msg = ipmi_alloc_recv_msg(user); + if (IS_ERR(recv_msg)) { + mutex_unlock(&intf->users_mutex); list_for_each_entry_safe(recv_msg, recv_msg2, &msgs, link) { list_del(&recv_msg->link); @@ -4442,11 +4495,9 @@ static int handle_read_event_rsp(struct ipmi_smi *intf, deliver_count++; copy_event_into_recv_msg(recv_msg, msg); - recv_msg->user = user; - kref_get(&user->refcount); list_add_tail(&recv_msg->link, &msgs); } - srcu_read_unlock(&intf->users_srcu, index); + mutex_unlock(&intf->users_mutex); if (deliver_count) { /* Now deliver all the messages. */ @@ -4459,8 +4510,8 @@ static int handle_read_event_rsp(struct ipmi_smi *intf, * No one to receive the message, put it in queue if there's * not already too many things in the queue. */ - recv_msg = ipmi_alloc_recv_msg(); - if (!recv_msg) { + recv_msg = ipmi_alloc_recv_msg(NULL); + if (IS_ERR(recv_msg)) { /* * We couldn't allocate memory for the * message, so requeue it for handling @@ -4484,7 +4535,7 @@ static int handle_read_event_rsp(struct ipmi_smi *intf, } out: - spin_unlock_irqrestore(&intf->events_lock, flags); + mutex_unlock(&intf->events_mutex); return rv; } @@ -4495,7 +4546,7 @@ static int handle_bmc_rsp(struct ipmi_smi *intf, struct ipmi_recv_msg *recv_msg; struct ipmi_system_interface_addr *smi_addr; - recv_msg = msg->user_data; + recv_msg = msg->recv_msg; if (recv_msg == NULL) { dev_warn(intf->si_dev, "IPMI SMI message received with no owner. This could be because of a malformed message, or because of a hardware error. Contact your hardware vendor for assistance.\n"); @@ -4536,9 +4587,10 @@ static int handle_one_recv_msg(struct ipmi_smi *intf, if (msg->rsp_size < 2) { /* Message is too small to be correct. */ - dev_warn(intf->si_dev, - "BMC returned too small a message for netfn %x cmd %x, got %d bytes\n", - (msg->data[0] >> 2) | 1, msg->data[1], msg->rsp_size); + dev_warn_ratelimited(intf->si_dev, + "BMC returned too small a message for netfn %x cmd %x, got %d bytes\n", + (msg->data[0] >> 2) | 1, + msg->data[1], msg->rsp_size); return_unspecified: /* Generate an error response for the message. */ @@ -4568,14 +4620,14 @@ return_unspecified: } else if ((msg->data_size >= 2) && (msg->data[0] == (IPMI_NETFN_APP_REQUEST << 2)) && (msg->data[1] == IPMI_SEND_MSG_CMD) - && (msg->user_data == NULL)) { + && (msg->recv_msg == NULL)) { - if (intf->in_shutdown) + if (intf->in_shutdown || intf->run_to_completion) goto out; /* * This is the local response to a command send, start - * the timer for these. The user_data will not be + * the timer for these. The recv_msg will not be * NULL if this is a response send, and we will let * response sends just go through. */ @@ -4614,10 +4666,10 @@ return_unspecified: * The NetFN and Command in the response is not even * marginally correct. */ - dev_warn(intf->si_dev, - "BMC returned incorrect response, expected netfn %x cmd %x, got netfn %x cmd %x\n", - (msg->data[0] >> 2) | 1, msg->data[1], - msg->rsp[0] >> 2, msg->rsp[1]); + dev_warn_ratelimited(intf->si_dev, + "BMC returned incorrect response, expected netfn %x cmd %x, got netfn %x cmd %x\n", + (msg->data[0] >> 2) | 1, msg->data[1], + msg->rsp[0] >> 2, msg->rsp[1]); goto return_unspecified; } @@ -4635,13 +4687,16 @@ return_unspecified: requeue = handle_ipmb_direct_rcv_rsp(intf, msg); } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) && (msg->rsp[1] == IPMI_SEND_MSG_CMD) - && (msg->user_data != NULL)) { + && (msg->recv_msg != NULL)) { /* * It's a response to a response we sent. For this we * deliver a send message response to the user. */ struct ipmi_recv_msg *recv_msg; + if (intf->run_to_completion) + goto out; + chan = msg->data[2] & 0x0f; if (chan >= IPMI_MAX_CHANNELS) /* Invalid channel number */ @@ -4649,7 +4704,7 @@ return_unspecified: cc = msg->rsp[2]; process_response_response: - recv_msg = msg->user_data; + recv_msg = msg->recv_msg; requeue = 0; if (!recv_msg) @@ -4664,6 +4719,9 @@ process_response_response: && (msg->rsp[1] == IPMI_GET_MSG_CMD)) { struct ipmi_channel *chans; + if (intf->run_to_completion) + goto out; + /* It's from the receive queue. */ chan = msg->rsp[3] & 0xf; if (chan >= IPMI_MAX_CHANNELS) { @@ -4738,6 +4796,9 @@ process_response_response: } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) && (msg->rsp[1] == IPMI_READ_EVENT_MSG_BUFFER_CMD)) { /* It's an asynchronous event. */ + if (intf->run_to_completion) + goto out; + requeue = handle_read_event_rsp(intf, msg); } else { /* It's a response from the local BMC. */ @@ -4753,10 +4814,10 @@ process_response_response: */ static void handle_new_recv_msgs(struct ipmi_smi *intf) { - struct ipmi_smi_msg *smi_msg; - unsigned long flags = 0; - int rv; - int run_to_completion = intf->run_to_completion; + struct ipmi_smi_msg *smi_msg; + unsigned long flags = 0; + int rv; + int run_to_completion = READ_ONCE(intf->run_to_completion); /* See if any waiting messages need to be processed. */ if (!run_to_completion) @@ -4790,31 +4851,16 @@ static void handle_new_recv_msgs(struct ipmi_smi *intf) } if (!run_to_completion) spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock, flags); - - /* - * If the pretimout count is non-zero, decrement one from it and - * deliver pretimeouts to all the users. - */ - if (atomic_add_unless(&intf->watchdog_pretimeouts_to_deliver, -1, 0)) { - struct ipmi_user *user; - int index; - - index = srcu_read_lock(&intf->users_srcu); - list_for_each_entry_rcu(user, &intf->users, link) { - if (user->handler->ipmi_watchdog_pretimeout) - user->handler->ipmi_watchdog_pretimeout( - user->handler_data); - } - srcu_read_unlock(&intf->users_srcu, index); - } } -static void smi_recv_work(struct work_struct *t) +static void smi_work(struct work_struct *t) { unsigned long flags = 0; /* keep us warning-free. */ - struct ipmi_smi *intf = from_work(intf, t, recv_work); - int run_to_completion = intf->run_to_completion; + struct ipmi_smi *intf = from_work(intf, t, smi_work); + int run_to_completion = READ_ONCE(intf->run_to_completion); struct ipmi_smi_msg *newmsg = NULL; + struct ipmi_recv_msg *msg, *msg2; + int cc; /* * Start the next message if available. @@ -4823,11 +4869,8 @@ static void smi_recv_work(struct work_struct *t) * because the lower layer is allowed to hold locks while calling * message delivery. */ - - rcu_read_lock(); - - if (!run_to_completion) - spin_lock_irqsave(&intf->xmit_msgs_lock, flags); +restart: + ipmi_lock_xmit_msgs(intf, run_to_completion, &flags); if (intf->curr_msg == NULL && !intf->in_shutdown) { struct list_head *entry = NULL; @@ -4843,15 +4886,66 @@ static void smi_recv_work(struct work_struct *t) intf->curr_msg = newmsg; } } + ipmi_unlock_xmit_msgs(intf, run_to_completion, &flags); + + if (newmsg) { + cc = intf->handlers->sender(intf->send_info, newmsg); + if (cc) { + if (newmsg->recv_msg) + deliver_err_response(intf, + newmsg->recv_msg, cc); + ipmi_lock_xmit_msgs(intf, run_to_completion, &flags); + intf->curr_msg = NULL; + ipmi_unlock_xmit_msgs(intf, run_to_completion, &flags); + ipmi_free_smi_msg(newmsg); + newmsg = NULL; + goto restart; + } + } - if (!run_to_completion) - spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags); - if (newmsg) - intf->handlers->sender(intf->send_info, newmsg); + handle_new_recv_msgs(intf); - rcu_read_unlock(); + /* Nothing below applies during panic time. */ + if (run_to_completion) + return; - handle_new_recv_msgs(intf); + /* + * If the pretimout count is non-zero, decrement one from it and + * deliver pretimeouts to all the users. + */ + if (atomic_add_unless(&intf->watchdog_pretimeouts_to_deliver, -1, 0)) { + struct ipmi_user *user; + + mutex_lock(&intf->users_mutex); + list_for_each_entry(user, &intf->users, link) { + if (user->handler->ipmi_watchdog_pretimeout) + user->handler->ipmi_watchdog_pretimeout( + user->handler_data); + } + mutex_unlock(&intf->users_mutex); + } + + /* + * Freeing the message can cause a user to be released, which + * can then cause the interface to be freed. Make sure that + * doesn't happen until we are ready. + */ + kref_get(&intf->refcount); + + mutex_lock(&intf->user_msgs_mutex); + list_for_each_entry_safe(msg, msg2, &intf->user_msgs, link) { + struct ipmi_user *user = msg->user; + + list_del(&msg->link); + + if (refcount_read(&user->destroyed) == 0) + ipmi_free_recv_msg(msg); + else + user->handler->ipmi_recv_hndl(msg, user->handler_data); + } + mutex_unlock(&intf->user_msgs_mutex); + + kref_put(&intf->refcount, intf_free); } /* Handle a new message from the lower layer. */ @@ -4859,7 +4953,7 @@ void ipmi_smi_msg_received(struct ipmi_smi *intf, struct ipmi_smi_msg *msg) { unsigned long flags = 0; /* keep us warning-free. */ - int run_to_completion = intf->run_to_completion; + int run_to_completion = READ_ONCE(intf->run_to_completion); /* * To preserve message order, we keep a queue and deliver from @@ -4872,21 +4966,19 @@ void ipmi_smi_msg_received(struct ipmi_smi *intf, spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock, flags); - if (!run_to_completion) - spin_lock_irqsave(&intf->xmit_msgs_lock, flags); + ipmi_lock_xmit_msgs(intf, run_to_completion, &flags); /* * We can get an asynchronous event or receive message in addition * to commands we send. */ if (msg == intf->curr_msg) intf->curr_msg = NULL; - if (!run_to_completion) - spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags); + ipmi_unlock_xmit_msgs(intf, run_to_completion, &flags); if (run_to_completion) - smi_recv_work(&intf->recv_work); + smi_work(&intf->smi_work); else - queue_work(system_bh_wq, &intf->recv_work); + queue_work(system_percpu_wq, &intf->smi_work); } EXPORT_SYMBOL(ipmi_smi_msg_received); @@ -4896,7 +4988,7 @@ void ipmi_smi_watchdog_pretimeout(struct ipmi_smi *intf) return; atomic_set(&intf->watchdog_pretimeouts_to_deliver, 1); - queue_work(system_bh_wq, &intf->recv_work); + queue_work(system_percpu_wq, &intf->smi_work); } EXPORT_SYMBOL(ipmi_smi_watchdog_pretimeout); @@ -4925,8 +5017,7 @@ smi_from_recv_msg(struct ipmi_smi *intf, struct ipmi_recv_msg *recv_msg, static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent, struct list_head *timeouts, unsigned long timeout_period, - int slot, unsigned long *flags, - bool *need_timer) + int slot, bool *need_timer) { struct ipmi_recv_msg *msg; @@ -4978,7 +5069,7 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent, return; } - spin_unlock_irqrestore(&intf->seq_lock, *flags); + mutex_unlock(&intf->seq_lock); /* * Send the new message. We send with a zero @@ -4995,18 +5086,23 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent, ipmi_inc_stat(intf, retransmitted_ipmb_commands); - smi_send(intf, intf->handlers, smi_msg, 0); + /* If this fails we'll retry later or timeout. */ + if (smi_send(intf, intf->handlers, smi_msg, 0) != IPMI_CC_NO_ERROR) { + /* But fix the timeout. */ + intf_start_seq_timer(intf, smi_msg->msgid); + ipmi_free_smi_msg(smi_msg); + } } else ipmi_free_smi_msg(smi_msg); - spin_lock_irqsave(&intf->seq_lock, *flags); + mutex_lock(&intf->seq_lock); } } static bool ipmi_timeout_handler(struct ipmi_smi *intf, unsigned long timeout_period) { - struct list_head timeouts; + LIST_HEAD(timeouts); struct ipmi_recv_msg *msg, *msg2; unsigned long flags; int i; @@ -5025,8 +5121,7 @@ static bool ipmi_timeout_handler(struct ipmi_smi *intf, * have timed out, putting them in the timeouts * list. */ - INIT_LIST_HEAD(&timeouts); - spin_lock_irqsave(&intf->seq_lock, flags); + mutex_lock(&intf->seq_lock); if (intf->ipmb_maintenance_mode_timeout) { if (intf->ipmb_maintenance_mode_timeout <= timeout_period) intf->ipmb_maintenance_mode_timeout = 0; @@ -5036,8 +5131,8 @@ static bool ipmi_timeout_handler(struct ipmi_smi *intf, for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) check_msg_timeout(intf, &intf->seq_table[i], &timeouts, timeout_period, i, - &flags, &need_timer); - spin_unlock_irqrestore(&intf->seq_lock, flags); + &need_timer); + mutex_unlock(&intf->seq_lock); list_for_each_entry_safe(msg, msg2, &timeouts, link) deliver_err_response(intf, msg, IPMI_TIMEOUT_COMPLETION_CODE); @@ -5057,7 +5152,9 @@ static bool ipmi_timeout_handler(struct ipmi_smi *intf, -= timeout_period; if (!intf->maintenance_mode && (intf->auto_maintenance_timeout <= 0)) { - intf->maintenance_mode_enable = false; + intf->maintenance_mode_state = + IPMI_MAINTENANCE_MODE_STATE_OFF; + intf->auto_maintenance_timeout = 0; maintenance_mode_update(intf); } } @@ -5065,7 +5162,7 @@ static bool ipmi_timeout_handler(struct ipmi_smi *intf, flags); } - queue_work(system_bh_wq, &intf->recv_work); + queue_work(system_percpu_wq, &intf->smi_work); return need_timer; } @@ -5073,28 +5170,28 @@ static bool ipmi_timeout_handler(struct ipmi_smi *intf, static void ipmi_request_event(struct ipmi_smi *intf) { /* No event requests when in maintenance mode. */ - if (intf->maintenance_mode_enable) + if (intf->maintenance_mode_state) return; if (!intf->in_shutdown) intf->handlers->request_events(intf->send_info); } -static struct timer_list ipmi_timer; - static atomic_t stop_operation; -static void ipmi_timeout(struct timer_list *unused) +static void ipmi_timeout_work(struct work_struct *work) { + if (atomic_read(&stop_operation)) + return; + struct ipmi_smi *intf; bool need_timer = false; - int index; if (atomic_read(&stop_operation)) return; - index = srcu_read_lock(&ipmi_interfaces_srcu); - list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { + mutex_lock(&ipmi_interfaces_mutex); + list_for_each_entry(intf, &ipmi_interfaces, link) { if (atomic_read(&intf->event_waiters)) { intf->ticks_to_req_ev--; if (intf->ticks_to_req_ev == 0) { @@ -5103,15 +5200,27 @@ static void ipmi_timeout(struct timer_list *unused) } need_timer = true; } + if (intf->maintenance_mode_state) + need_timer = true; need_timer |= ipmi_timeout_handler(intf, IPMI_TIMEOUT_TIME); } - srcu_read_unlock(&ipmi_interfaces_srcu, index); + mutex_unlock(&ipmi_interfaces_mutex); if (need_timer) mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); } +static DECLARE_WORK(ipmi_timer_work, ipmi_timeout_work); + +static void ipmi_timeout(struct timer_list *unused) +{ + if (atomic_read(&stop_operation)) + return; + + queue_work(system_percpu_wq, &ipmi_timer_work); +} + static void need_waiter(struct ipmi_smi *intf) { /* Racy, but worst case we start the timer twice. */ @@ -5133,10 +5242,10 @@ static void free_smi_msg(struct ipmi_smi_msg *msg) struct ipmi_smi_msg *ipmi_alloc_smi_msg(void) { struct ipmi_smi_msg *rv; - rv = kmalloc(sizeof(struct ipmi_smi_msg), GFP_ATOMIC); + rv = kmalloc_obj(struct ipmi_smi_msg, GFP_ATOMIC); if (rv) { rv->done = free_smi_msg; - rv->user_data = NULL; + rv->recv_msg = NULL; rv->type = IPMI_SMI_MSG_TYPE_NORMAL; atomic_inc(&smi_msg_inuse_count); } @@ -5152,27 +5261,51 @@ static void free_recv_msg(struct ipmi_recv_msg *msg) kfree(msg); } -static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void) +static struct ipmi_recv_msg *ipmi_alloc_recv_msg(struct ipmi_user *user) { struct ipmi_recv_msg *rv; - rv = kmalloc(sizeof(struct ipmi_recv_msg), GFP_ATOMIC); - if (rv) { - rv->user = NULL; - rv->done = free_recv_msg; - atomic_inc(&recv_msg_inuse_count); + if (user) { + if (atomic_add_return(1, &user->nr_msgs) > max_msgs_per_user) { + atomic_dec(&user->nr_msgs); + return ERR_PTR(-EBUSY); + } + } + + rv = kmalloc_obj(struct ipmi_recv_msg, GFP_ATOMIC); + if (!rv) { + if (user) + atomic_dec(&user->nr_msgs); + return ERR_PTR(-ENOMEM); } + + rv->user = user; + rv->done = free_recv_msg; + if (user) + kref_get(&user->refcount); + atomic_inc(&recv_msg_inuse_count); return rv; } void ipmi_free_recv_msg(struct ipmi_recv_msg *msg) { - if (msg->user && !oops_in_progress) - kref_put(&msg->user->refcount, free_user); + if (msg->user && !oops_in_progress) { + atomic_dec(&msg->user->nr_msgs); + kref_put(&msg->user->refcount, free_ipmi_user); + } msg->done(msg); } EXPORT_SYMBOL(ipmi_free_recv_msg); +static void ipmi_set_recv_msg_user(struct ipmi_recv_msg *msg, + struct ipmi_user *user) +{ + WARN_ON_ONCE(msg->user); /* User should not be set. */ + msg->user = user; + atomic_inc(&user->nr_msgs); + kref_get(&user->refcount); +} + static atomic_t panic_done_count = ATOMIC_INIT(0); static void dummy_smi_done_handler(struct ipmi_smi_msg *msg) @@ -5188,9 +5321,9 @@ static void dummy_recv_done_handler(struct ipmi_recv_msg *msg) /* * Inside a panic, send a message and wait for a response. */ -static void ipmi_panic_request_and_wait(struct ipmi_smi *intf, - struct ipmi_addr *addr, - struct kernel_ipmi_msg *msg) +static void _ipmi_panic_request_and_wait(struct ipmi_smi *intf, + struct ipmi_addr *addr, + struct kernel_ipmi_msg *msg) { struct ipmi_smi_msg smi_msg; struct ipmi_recv_msg recv_msg; @@ -5220,6 +5353,15 @@ static void ipmi_panic_request_and_wait(struct ipmi_smi *intf, ipmi_poll(intf); } +void ipmi_panic_request_and_wait(struct ipmi_user *user, + struct ipmi_addr *addr, + struct kernel_ipmi_msg *msg) +{ + user->intf->run_to_completion = 1; + _ipmi_panic_request_and_wait(user->intf, addr, msg); +} +EXPORT_SYMBOL(ipmi_panic_request_and_wait); + static void event_receiver_fetcher(struct ipmi_smi *intf, struct ipmi_recv_msg *msg) { @@ -5288,7 +5430,7 @@ static void send_panic_events(struct ipmi_smi *intf, char *str) } /* Send the event announcing the panic. */ - ipmi_panic_request_and_wait(intf, &addr, &msg); + _ipmi_panic_request_and_wait(intf, &addr, &msg); /* * On every interface, dump a bunch of OEM event holding the @@ -5324,7 +5466,7 @@ static void send_panic_events(struct ipmi_smi *intf, char *str) msg.data = NULL; msg.data_len = 0; intf->null_user_handler = device_id_fetcher; - ipmi_panic_request_and_wait(intf, &addr, &msg); + _ipmi_panic_request_and_wait(intf, &addr, &msg); if (intf->local_event_generator) { /* Request the event receiver from the local MC. */ @@ -5333,7 +5475,7 @@ static void send_panic_events(struct ipmi_smi *intf, char *str) msg.data = NULL; msg.data_len = 0; intf->null_user_handler = event_receiver_fetcher; - ipmi_panic_request_and_wait(intf, &addr, &msg); + _ipmi_panic_request_and_wait(intf, &addr, &msg); } intf->null_user_handler = NULL; @@ -5385,7 +5527,7 @@ static void send_panic_events(struct ipmi_smi *intf, char *str) memcpy_and_pad(data+5, 11, p, size, '\0'); p += size; - ipmi_panic_request_and_wait(intf, &addr, &msg); + _ipmi_panic_request_and_wait(intf, &addr, &msg); } } @@ -5403,7 +5545,7 @@ static int panic_event(struct notifier_block *this, has_panicked = 1; /* For every registered interface, set it to run to completion. */ - list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { + list_for_each_entry(intf, &ipmi_interfaces, link) { if (!intf->handlers || intf->intf_num == -1) /* Interface is not ready. */ continue; @@ -5433,7 +5575,7 @@ static int panic_event(struct notifier_block *this, intf->handlers->set_run_to_completion(intf->send_info, 1); - list_for_each_entry_rcu(user, &intf->users, link) { + list_for_each_entry(user, &intf->users, link) { if (user->handler->ipmi_panic_handler) user->handler->ipmi_panic_handler( user->handler_data); @@ -5478,15 +5620,11 @@ static int ipmi_init_msghandler(void) if (initialized) goto out; - rv = init_srcu_struct(&ipmi_interfaces_srcu); - if (rv) - goto out; - - remove_work_wq = create_singlethread_workqueue("ipmi-msghandler-remove-wq"); - if (!remove_work_wq) { + bmc_remove_work_wq = create_singlethread_workqueue("ipmi-msghandler-remove-wq"); + if (!bmc_remove_work_wq) { pr_err("unable to create ipmi-msghandler-remove-wq workqueue"); rv = -ENOMEM; - goto out_wq; + goto out; } timer_setup(&ipmi_timer, ipmi_timeout, 0); @@ -5496,9 +5634,6 @@ static int ipmi_init_msghandler(void) initialized = true; -out_wq: - if (rv) - cleanup_srcu_struct(&ipmi_interfaces_srcu); out: mutex_unlock(&ipmi_interfaces_mutex); return rv; @@ -5522,7 +5657,7 @@ static void __exit cleanup_ipmi(void) int count; if (initialized) { - destroy_workqueue(remove_work_wq); + destroy_workqueue(bmc_remove_work_wq); atomic_notifier_chain_unregister(&panic_notifier_list, &panic_block); @@ -5538,7 +5673,8 @@ static void __exit cleanup_ipmi(void) * here. */ atomic_set(&stop_operation, 1); - del_timer_sync(&ipmi_timer); + timer_delete_sync(&ipmi_timer); + cancel_work_sync(&ipmi_timer_work); initialized = false; @@ -5549,8 +5685,6 @@ static void __exit cleanup_ipmi(void) count = atomic_read(&recv_msg_inuse_count); if (count != 0) pr_warn("recv message count %d at exit\n", count); - - cleanup_srcu_struct(&ipmi_interfaces_srcu); } if (drvregistered) driver_unregister(&ipmidriver.driver); diff --git a/drivers/char/ipmi/ipmi_powernv.c b/drivers/char/ipmi/ipmi_powernv.c index 4a2efafcd1f8..52a1130defe5 100644 --- a/drivers/char/ipmi/ipmi_powernv.c +++ b/drivers/char/ipmi/ipmi_powernv.c @@ -51,7 +51,7 @@ static void send_error_reply(struct ipmi_smi_powernv *smi, ipmi_smi_msg_received(smi->intf, msg); } -static void ipmi_powernv_send(void *send_info, struct ipmi_smi_msg *msg) +static int ipmi_powernv_send(void *send_info, struct ipmi_smi_msg *msg) { struct ipmi_smi_powernv *smi = send_info; struct opal_ipmi_msg *opal_msg; @@ -93,18 +93,19 @@ static void ipmi_powernv_send(void *send_info, struct ipmi_smi_msg *msg) smi->interface_id, opal_msg, size); rc = opal_ipmi_send(smi->interface_id, opal_msg, size); pr_devel("%s: -> %d\n", __func__, rc); - - if (!rc) { - smi->cur_msg = msg; - spin_unlock_irqrestore(&smi->msg_lock, flags); - return; + if (rc) { + comp = IPMI_ERR_UNSPECIFIED; + goto err_unlock; } - comp = IPMI_ERR_UNSPECIFIED; + smi->cur_msg = msg; + spin_unlock_irqrestore(&smi->msg_lock, flags); + return IPMI_CC_NO_ERROR; + err_unlock: spin_unlock_irqrestore(&smi->msg_lock, flags); err: - send_error_reply(smi, msg, comp); + return comp; } static int ipmi_powernv_recv(struct ipmi_smi_powernv *smi) diff --git a/drivers/char/ipmi/ipmi_si.h b/drivers/char/ipmi/ipmi_si.h index a7ead2a4c753..687835b53da5 100644 --- a/drivers/char/ipmi/ipmi_si.h +++ b/drivers/char/ipmi/ipmi_si.h @@ -26,6 +26,14 @@ enum si_type { /* Array is defined in the ipmi_si_intf.c */ extern const char *const si_to_str[]; +struct ipmi_match_info { + enum si_type type; +}; + +extern const struct ipmi_match_info ipmi_kcs_si_info; +extern const struct ipmi_match_info ipmi_smic_si_info; +extern const struct ipmi_match_info ipmi_bt_si_info; + enum ipmi_addr_space { IPMI_IO_ADDR_SPACE, IPMI_MEM_ADDR_SPACE }; @@ -64,7 +72,7 @@ struct si_sm_io { void (*irq_cleanup)(struct si_sm_io *io); u8 slave_addr; - enum si_type si_type; + const struct ipmi_match_info *si_info; struct device *dev; }; @@ -93,6 +101,13 @@ void ipmi_si_pci_shutdown(void); static inline void ipmi_si_pci_init(void) { } static inline void ipmi_si_pci_shutdown(void) { } #endif +#ifdef CONFIG_IPMI_LS2K +void ipmi_si_ls2k_init(void); +void ipmi_si_ls2k_shutdown(void); +#else +static inline void ipmi_si_ls2k_init(void) { } +static inline void ipmi_si_ls2k_shutdown(void) { } +#endif #ifdef CONFIG_PARISC void ipmi_si_parisc_init(void); void ipmi_si_parisc_shutdown(void); diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index eea23a3b966e..9a9d12be9bf7 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -53,6 +53,7 @@ #define SI_TIMEOUT_JIFFIES (SI_TIMEOUT_TIME_USEC/SI_USEC_PER_JIFFY) #define SI_SHORT_TIMEOUT_USEC 250 /* .25ms when the SM request a short timeout */ +#define SI_TIMEOUT_HOSED (HZ) /* 1 second when in hosed state. */ enum si_intf_state { SI_NORMAL, @@ -61,7 +62,8 @@ enum si_intf_state { SI_CLEARING_FLAGS, SI_GETTING_MESSAGES, SI_CHECKING_ENABLES, - SI_SETTING_ENABLES + SI_SETTING_ENABLES, + SI_HOSED /* FIXME - add watchdog stuff. */ }; @@ -73,6 +75,10 @@ enum si_intf_state { /* 'invalid' to allow a firmware-specified interface to be disabled */ const char *const si_to_str[] = { "invalid", "kcs", "smic", "bt", NULL }; +const struct ipmi_match_info ipmi_kcs_si_info = { .type = SI_KCS }; +const struct ipmi_match_info ipmi_smic_si_info = { .type = SI_SMIC }; +const struct ipmi_match_info ipmi_bt_si_info = { .type = SI_BT }; + static bool initialized; /* @@ -162,6 +168,10 @@ struct smi_info { OEM2_DATA_AVAIL) unsigned char msg_flags; + /* When requesting events and messages, don't do it forever. */ + unsigned int num_requests_in_a_row; + bool last_was_flag_fetch; + /* Does the BMC have an event buffer? */ bool has_event_buffer; @@ -269,8 +279,7 @@ void debug_timestamp(struct smi_info *smi_info, char *msg) struct timespec64 t; ktime_get_ts64(&t); - dev_dbg(smi_info->io.dev, "**%s: %lld.%9.9ld\n", - msg, t.tv_sec, t.tv_nsec); + dev_dbg(smi_info->io.dev, "**%s: %ptSp\n", msg, &t); } #else #define debug_timestamp(smi_info, x) @@ -309,7 +318,7 @@ static void return_hosed_msg(struct smi_info *smi_info, int cCode) static enum si_sm_result start_next_msg(struct smi_info *smi_info) { - int rv; + int rv; if (!smi_info->waiting_msg) { smi_info->curr_msg = NULL; @@ -386,6 +395,17 @@ static void start_clear_flags(struct smi_info *smi_info) smi_info->si_state = SI_CLEARING_FLAGS; } +static void start_get_flags(struct smi_info *smi_info) +{ + unsigned char msg[2]; + + msg[0] = (IPMI_NETFN_APP_REQUEST << 2); + msg[1] = IPMI_GET_MSG_FLAGS_CMD; + + start_new_msg(smi_info, msg, 2); + smi_info->si_state = SI_GETTING_FLAGS; +} + static void start_getting_msg_queue(struct smi_info *smi_info) { smi_info->curr_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); @@ -394,7 +414,10 @@ static void start_getting_msg_queue(struct smi_info *smi_info) start_new_msg(smi_info, smi_info->curr_msg->data, smi_info->curr_msg->data_size); - smi_info->si_state = SI_GETTING_MESSAGES; + if (smi_info->si_state != SI_GETTING_MESSAGES) { + smi_info->num_requests_in_a_row = 0; + smi_info->si_state = SI_GETTING_MESSAGES; + } } static void start_getting_events(struct smi_info *smi_info) @@ -405,7 +428,10 @@ static void start_getting_events(struct smi_info *smi_info) start_new_msg(smi_info, smi_info->curr_msg->data, smi_info->curr_msg->data_size); - smi_info->si_state = SI_GETTING_EVENTS; + if (smi_info->si_state != SI_GETTING_EVENTS) { + smi_info->num_requests_in_a_row = 0; + smi_info->si_state = SI_GETTING_EVENTS; + } } /* @@ -471,15 +497,19 @@ retry: } else if (smi_info->msg_flags & RECEIVE_MSG_AVAIL) { /* Messages available. */ smi_info->curr_msg = alloc_msg_handle_irq(smi_info); - if (!smi_info->curr_msg) + if (!smi_info->curr_msg) { + smi_info->si_state = SI_NORMAL; return; + } start_getting_msg_queue(smi_info); } else if (smi_info->msg_flags & EVENT_MSG_BUFFER_FULL) { /* Events available. */ smi_info->curr_msg = alloc_msg_handle_irq(smi_info); - if (!smi_info->curr_msg) + if (!smi_info->curr_msg) { + smi_info->si_state = SI_NORMAL; return; + } start_getting_events(smi_info); } else if (smi_info->msg_flags & OEM_DATA_AVAIL && @@ -579,6 +609,7 @@ static void handle_transaction_done(struct smi_info *smi_info) smi_info->si_state = SI_NORMAL; } else { smi_info->msg_flags = msg[3]; + smi_info->last_was_flag_fetch = true; handle_flags(smi_info); } break; @@ -614,7 +645,13 @@ static void handle_transaction_done(struct smi_info *smi_info) */ msg = smi_info->curr_msg; smi_info->curr_msg = NULL; - if (msg->rsp[2] != 0) { + /* + * It appears some BMCs, with no event data, return no + * data in the message and not a 0x80 error as the + * spec says they should. Shut down processing if + * the data is not the right length. + */ + if (msg->rsp[2] != 0 || msg->rsp_size != 19) { /* Error getting event, probably done. */ msg->done(msg); @@ -624,6 +661,11 @@ static void handle_transaction_done(struct smi_info *smi_info) } else { smi_inc_stat(smi_info, events); + smi_info->num_requests_in_a_row++; + if (smi_info->num_requests_in_a_row > 10) + /* Stop if we do this too many times. */ + smi_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL; + /* * Do this before we deliver the message * because delivering the message releases the @@ -662,6 +704,11 @@ static void handle_transaction_done(struct smi_info *smi_info) } else { smi_inc_stat(smi_info, incoming_messages); + smi_info->num_requests_in_a_row++; + if (smi_info->num_requests_in_a_row > 10) + /* Stop if we do this too many times. */ + smi_info->msg_flags &= ~RECEIVE_MSG_AVAIL; + /* * Do this before we deliver the message * because delivering the message releases the @@ -692,7 +739,7 @@ static void handle_transaction_done(struct smi_info *smi_info) break; } enables = current_global_enables(smi_info, 0, &irq_on); - if (smi_info->io.si_type == SI_BT) + if (smi_info->io.si_info->type == SI_BT) /* BT has its own interrupt enable bit. */ check_bt_irq(smi_info, irq_on); if (enables != (msg[3] & GLOBAL_ENABLES_MASK)) { @@ -738,6 +785,8 @@ static void handle_transaction_done(struct smi_info *smi_info) } break; } + case SI_HOSED: /* Shouldn't happen. */ + break; } } @@ -752,6 +801,10 @@ static enum si_sm_result smi_event_handler(struct smi_info *smi_info, enum si_sm_result si_sm_result; restart: + if (smi_info->si_state == SI_HOSED) + /* Just in case, hosed state is only left from the timeout. */ + return SI_SM_HOSED; + /* * There used to be a loop here that waited a little while * (around 25us) before giving up. That turned out to be @@ -775,18 +828,46 @@ restart: /* * Do the before return_hosed_msg, because that - * releases the lock. + * releases the lock. We just disable operations for + * a while and retry in hosed state. */ - smi_info->si_state = SI_NORMAL; + smi_info->si_state = SI_HOSED; if (smi_info->curr_msg != NULL) { /* * If we were handling a user message, format * a response to send to the upper layer to * tell it about the error. */ - return_hosed_msg(smi_info, IPMI_ERR_UNSPECIFIED); + return_hosed_msg(smi_info, IPMI_BUS_ERR); } - goto restart; + if (smi_info->waiting_msg != NULL) { + /* Also handle if there was a message waiting. */ + smi_info->curr_msg = smi_info->waiting_msg; + smi_info->waiting_msg = NULL; + return_hosed_msg(smi_info, IPMI_BUS_ERR); + } + smi_mod_timer(smi_info, jiffies + SI_TIMEOUT_HOSED); + goto out; + } + + /* + * If we are currently idle, or if the last thing that was + * done was a flag fetch and there is a message pending, try + * to start the next message. + * + * We do the waiting message check to avoid a stuck flag + * completely wedging the driver. Let a message through + * in between flag operations if that happens. + */ + if (si_sm_result == SI_SM_IDLE || + (si_sm_result == SI_SM_ATTN && smi_info->waiting_msg && + smi_info->last_was_flag_fetch)) { + smi_info->last_was_flag_fetch = false; + smi_inc_stat(smi_info, idles); + + si_sm_result = start_next_msg(smi_info); + if (si_sm_result != SI_SM_IDLE) + goto restart; } /* @@ -794,8 +875,6 @@ restart: * this if there is not yet an upper layer to handle anything. */ if (si_sm_result == SI_SM_ATTN || smi_info->got_attn) { - unsigned char msg[2]; - if (smi_info->si_state != SI_NORMAL) { /* * We got an ATTN, but we are doing something else. @@ -813,24 +892,11 @@ restart: * interrupts work with the SMI, that's not really * possible. */ - msg[0] = (IPMI_NETFN_APP_REQUEST << 2); - msg[1] = IPMI_GET_MSG_FLAGS_CMD; - - start_new_msg(smi_info, msg, 2); - smi_info->si_state = SI_GETTING_FLAGS; + start_get_flags(smi_info); goto restart; } } - /* If we are currently idle, try to start the next message. */ - if (si_sm_result == SI_SM_IDLE) { - smi_inc_stat(smi_info, idles); - - si_sm_result = start_next_msg(smi_info); - if (si_sm_result != SI_SM_IDLE) - goto restart; - } - if ((si_sm_result == SI_SM_IDLE) && (atomic_read(&smi_info->req_events))) { /* @@ -859,7 +925,7 @@ restart: if (si_sm_result == SI_SM_IDLE && smi_info->timer_running) { /* Ok it if fails, the timer will just go off. */ - if (del_timer(&smi_info->si_timer)) + if (timer_delete(&smi_info->si_timer)) smi_info->timer_running = false; } @@ -890,41 +956,46 @@ static void flush_messages(void *send_info) * mode. This means we are single-threaded, no need for locks. */ result = smi_event_handler(smi_info, 0); - while (result != SI_SM_IDLE) { + while (result != SI_SM_IDLE && result != SI_SM_HOSED) { udelay(SI_SHORT_TIMEOUT_USEC); result = smi_event_handler(smi_info, SI_SHORT_TIMEOUT_USEC); } } -static void sender(void *send_info, - struct ipmi_smi_msg *msg) +static int sender(void *send_info, struct ipmi_smi_msg *msg) { struct smi_info *smi_info = send_info; unsigned long flags; + int rv = IPMI_CC_NO_ERROR; debug_timestamp(smi_info, "Enqueue"); + /* + * Check here for run to completion mode. A check under lock is + * later. + */ + if (smi_info->si_state == SI_HOSED) + return IPMI_BUS_ERR; + if (smi_info->run_to_completion) { /* * If we are running to completion, start it. Upper * layer will call flush_messages to clear it out. */ smi_info->waiting_msg = msg; - return; + return IPMI_CC_NO_ERROR; } spin_lock_irqsave(&smi_info->si_lock, flags); - /* - * The following two lines don't need to be under the lock for - * the lock's sake, but they do need SMP memory barriers to - * avoid getting things out of order. We are already claiming - * the lock, anyway, so just do it under the lock to avoid the - * ordering problem. - */ - BUG_ON(smi_info->waiting_msg); - smi_info->waiting_msg = msg; - check_start_timer_thread(smi_info); + if (smi_info->si_state == SI_HOSED) { + rv = IPMI_BUS_ERR; + } else { + BUG_ON(smi_info->waiting_msg); + smi_info->waiting_msg = msg; + check_start_timer_thread(smi_info); + } spin_unlock_irqrestore(&smi_info->si_lock, flags); + return rv; } static void set_run_to_completion(void *send_info, bool i_run_to_completion) @@ -1072,7 +1143,8 @@ static void set_need_watch(void *send_info, unsigned int watch_mask) static void smi_timeout(struct timer_list *t) { - struct smi_info *smi_info = from_timer(smi_info, t, si_timer); + struct smi_info *smi_info = timer_container_of(smi_info, t, + si_timer); enum si_sm_result smi_result; unsigned long flags; unsigned long jiffies_now; @@ -1082,23 +1154,26 @@ static void smi_timeout(struct timer_list *t) spin_lock_irqsave(&(smi_info->si_lock), flags); debug_timestamp(smi_info, "Timer"); + if (smi_info->si_state == SI_HOSED) + /* Try something to see if the BMC is now operational. */ + start_get_flags(smi_info); + jiffies_now = jiffies; time_diff = (((long)jiffies_now - (long)smi_info->last_timeout_jiffies) * SI_USEC_PER_JIFFY); smi_result = smi_event_handler(smi_info, time_diff); - if ((smi_info->io.irq) && (!smi_info->interrupt_disabled)) { + if (smi_info->si_state == SI_HOSED) { + timeout = jiffies + SI_TIMEOUT_HOSED; + } else if ((smi_info->io.irq) && (!smi_info->interrupt_disabled)) { /* Running with interrupts, only do long timeouts. */ timeout = jiffies + SI_TIMEOUT_JIFFIES; smi_inc_stat(smi_info, long_timeouts); - goto do_mod_timer; - } - - /* - * If the state machine asks for a short delay, then shorten - * the timer timeout. - */ - if (smi_result == SI_SM_CALL_WITH_DELAY) { + } else if (smi_result == SI_SM_CALL_WITH_DELAY) { + /* + * If the state machine asks for a short delay, then shorten + * the timer timeout. + */ smi_inc_stat(smi_info, short_timeouts); timeout = jiffies + 1; } else { @@ -1106,7 +1181,6 @@ static void smi_timeout(struct timer_list *t) timeout = jiffies + SI_TIMEOUT_JIFFIES; } -do_mod_timer: if (smi_result != SI_SM_IDLE) smi_mod_timer(smi_info, timeout); else @@ -1119,7 +1193,7 @@ irqreturn_t ipmi_si_irq_handler(int irq, void *data) struct smi_info *smi_info = data; unsigned long flags; - if (smi_info->io.si_type == SI_BT) + if (smi_info->io.si_info->type == SI_BT) /* We need to clear the IRQ flag for the BT interface. */ smi_info->io.outputb(&smi_info->io, IPMI_BT_INTMASK_REG, IPMI_BT_INTMASK_CLEAR_IRQ_BIT @@ -1164,7 +1238,7 @@ static int smi_start_processing(void *send_info, * The BT interface is efficient enough to not need a thread, * and there is no need for a thread if we have interrupts. */ - else if ((new_smi->io.si_type != SI_BT) && (!new_smi->io.irq)) + else if (new_smi->io.si_info->type != SI_BT && !new_smi->io.irq) enable = 1; if (enable) { @@ -1235,7 +1309,7 @@ MODULE_PARM_DESC(kipmid_max_busy_us, void ipmi_irq_finish_setup(struct si_sm_io *io) { - if (io->si_type == SI_BT) + if (io->si_info->type == SI_BT) /* Enable the interrupt in the BT interface. */ io->outputb(io, IPMI_BT_INTMASK_REG, IPMI_BT_INTMASK_ENABLE_IRQ_BIT); @@ -1243,7 +1317,7 @@ void ipmi_irq_finish_setup(struct si_sm_io *io) void ipmi_irq_start_cleanup(struct si_sm_io *io) { - if (io->si_type == SI_BT) + if (io->si_info->type == SI_BT) /* Disable the interrupt in the BT interface. */ io->outputb(io, IPMI_BT_INTMASK_REG, 0); } @@ -1614,7 +1688,7 @@ static ssize_t type_show(struct device *dev, { struct smi_info *smi_info = dev_get_drvdata(dev); - return sysfs_emit(buf, "%s\n", si_to_str[smi_info->io.si_type]); + return sysfs_emit(buf, "%s\n", si_to_str[smi_info->io.si_info->type]); } static DEVICE_ATTR_RO(type); @@ -1649,7 +1723,7 @@ static ssize_t params_show(struct device *dev, return sysfs_emit(buf, "%s,%s,0x%lx,rsp=%d,rsi=%d,rsh=%d,irq=%d,ipmb=%d\n", - si_to_str[smi_info->io.si_type], + si_to_str[smi_info->io.si_info->type], addr_space_to_str[smi_info->io.addr_space], smi_info->io.addr_data, smi_info->io.regspacing, @@ -1803,7 +1877,7 @@ setup_dell_poweredge_bt_xaction_handler(struct smi_info *smi_info) { struct ipmi_device_id *id = &smi_info->device_id; if (id->manufacturer_id == DELL_IANA_MFR_ID && - smi_info->io.si_type == SI_BT) + smi_info->io.si_info->type == SI_BT) register_xaction_notifier(&dell_poweredge_bt_xaction_notifier); } @@ -1839,7 +1913,7 @@ static inline void stop_timer_and_thread(struct smi_info *smi_info) } smi_info->timer_can_start = false; - del_timer_sync(&smi_info->si_timer); + timer_delete_sync(&smi_info->si_timer); } static struct smi_info *find_dup_si(struct smi_info *info) @@ -1892,7 +1966,7 @@ int ipmi_si_add_smi(struct si_sm_io *io) } } - new_smi = kzalloc(sizeof(*new_smi), GFP_KERNEL); + new_smi = kzalloc_obj(*new_smi); if (!new_smi) return -ENOMEM; spin_lock_init(&new_smi->si_lock); @@ -1907,13 +1981,13 @@ int ipmi_si_add_smi(struct si_sm_io *io) /* We prefer ACPI over SMBIOS. */ dev_info(dup->io.dev, "Removing SMBIOS-specified %s state machine in favor of ACPI\n", - si_to_str[new_smi->io.si_type]); + si_to_str[new_smi->io.si_info->type]); cleanup_one_si(dup); } else { dev_info(new_smi->io.dev, "%s-specified %s state machine: duplicate\n", ipmi_addr_src_to_str(new_smi->io.addr_source), - si_to_str[new_smi->io.si_type]); + si_to_str[new_smi->io.si_info->type]); rv = -EBUSY; kfree(new_smi); goto out_err; @@ -1922,7 +1996,7 @@ int ipmi_si_add_smi(struct si_sm_io *io) pr_info("Adding %s-specified %s state machine\n", ipmi_addr_src_to_str(new_smi->io.addr_source), - si_to_str[new_smi->io.si_type]); + si_to_str[new_smi->io.si_info->type]); list_add_tail(&new_smi->link, &smi_infos); @@ -1945,12 +2019,12 @@ static int try_smi_init(struct smi_info *new_smi) pr_info("Trying %s-specified %s state machine at %s address 0x%lx, slave address 0x%x, irq %d\n", ipmi_addr_src_to_str(new_smi->io.addr_source), - si_to_str[new_smi->io.si_type], + si_to_str[new_smi->io.si_info->type], addr_space_to_str[new_smi->io.addr_space], new_smi->io.addr_data, new_smi->io.slave_addr, new_smi->io.irq); - switch (new_smi->io.si_type) { + switch (new_smi->io.si_info->type) { case SI_KCS: new_smi->handlers = &kcs_smi_handlers; break; @@ -2073,7 +2147,7 @@ static int try_smi_init(struct smi_info *new_smi) smi_num++; dev_info(new_smi->io.dev, "IPMI %s interface initialized\n", - si_to_str[new_smi->io.si_type]); + si_to_str[new_smi->io.si_info->type]); WARN_ON(new_smi->io.dev->init_name != NULL); @@ -2091,10 +2165,18 @@ static int try_smi_init(struct smi_info *new_smi) return rv; } +/* + * Devices in the same address space at the same address are the same. + */ +static bool __init ipmi_smi_info_same(struct smi_info *e1, struct smi_info *e2) +{ + return (e1->io.addr_space == e2->io.addr_space && + e1->io.addr_data == e2->io.addr_data); +} + static int __init init_ipmi_si(void) { - struct smi_info *e; - enum ipmi_addr_src type = SI_INVALID; + struct smi_info *e, *e2; if (initialized) return 0; @@ -2107,45 +2189,77 @@ static int __init init_ipmi_si(void) ipmi_si_pci_init(); + ipmi_si_ls2k_init(); + ipmi_si_parisc_init(); - /* We prefer devices with interrupts, but in the case of a machine - with multiple BMCs we assume that there will be several instances - of a given type so if we succeed in registering a type then also - try to register everything else of the same type */ mutex_lock(&smi_infos_lock); + + /* + * Scan through all the devices. We prefer devices with + * interrupts, so go through those first in case there are any + * duplicates that don't have the interrupt set. + */ list_for_each_entry(e, &smi_infos, link) { - /* Try to register a device if it has an IRQ and we either - haven't successfully registered a device yet or this - device has the same type as one we successfully registered */ - if (e->io.irq && (!type || e->io.addr_source == type)) { - if (!try_smi_init(e)) { - type = e->io.addr_source; + bool dup = false; + + /* Register ones with interrupts first. */ + if (!e->io.irq) + continue; + + /* + * Go through the ones we have already seen to see if this + * is a dup. + */ + list_for_each_entry(e2, &smi_infos, link) { + if (e2 == e) + break; + if (e2->io.irq && ipmi_smi_info_same(e, e2)) { + dup = true; + break; } } + if (!dup) + try_smi_init(e); } - /* type will only have been set if we successfully registered an si */ - if (type) - goto skip_fallback_noirq; + /* + * Now try devices without interrupts. + */ + list_for_each_entry(e, &smi_infos, link) { + bool dup = false; - /* Fall back to the preferred device */ + if (e->io.irq) + continue; - list_for_each_entry(e, &smi_infos, link) { - if (!e->io.irq && (!type || e->io.addr_source == type)) { - if (!try_smi_init(e)) { - type = e->io.addr_source; + /* + * Go through the ones we have already seen to see if + * this is a dup. We have already looked at the ones + * with interrupts. + */ + list_for_each_entry(e2, &smi_infos, link) { + if (!e2->io.irq) + continue; + if (ipmi_smi_info_same(e, e2)) { + dup = true; + break; + } + } + list_for_each_entry(e2, &smi_infos, link) { + if (e2 == e) + break; + if (ipmi_smi_info_same(e, e2)) { + dup = true; + break; } } + if (!dup) + try_smi_init(e); } -skip_fallback_noirq: initialized = true; mutex_unlock(&smi_infos_lock); - if (type) - return 0; - mutex_lock(&smi_infos_lock); if (unload_when_empty && list_empty(&smi_infos)) { mutex_unlock(&smi_infos_lock); @@ -2164,7 +2278,8 @@ static void wait_msg_processed(struct smi_info *smi_info) unsigned long jiffies_now; long time_diff; - while (smi_info->curr_msg || (smi_info->si_state != SI_NORMAL)) { + while (smi_info->si_state != SI_HOSED && + (smi_info->curr_msg || (smi_info->si_state != SI_NORMAL))) { jiffies_now = jiffies; time_diff = (((long)jiffies_now - (long)smi_info->last_timeout_jiffies) * SI_USEC_PER_JIFFY); @@ -2267,7 +2382,7 @@ struct device *ipmi_si_remove_by_data(int addr_space, enum si_type si_type, list_for_each_entry_safe(e, tmp_e, &smi_infos, link) { if (e->io.addr_space != addr_space) continue; - if (e->io.si_type != si_type) + if (e->io.si_info->type != si_type) continue; if (e->io.addr_data == addr) { dev = get_device(e->io.dev); @@ -2288,6 +2403,8 @@ static void cleanup_ipmi_si(void) ipmi_si_pci_shutdown(); + ipmi_si_ls2k_shutdown(); + ipmi_si_parisc_shutdown(); ipmi_si_platform_shutdown(); diff --git a/drivers/char/ipmi/ipmi_si_ls2k.c b/drivers/char/ipmi/ipmi_si_ls2k.c new file mode 100644 index 000000000000..4c1da80f256c --- /dev/null +++ b/drivers/char/ipmi/ipmi_si_ls2k.c @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Loongson-2K BMC IPMI interface + * + * Copyright (C) 2024-2025 Loongson Technology Corporation Limited. + * + * Authors: + * Chong Qiao <qiaochong@loongson.cn> + * Binbin Zhou <zhoubinbin@loongson.cn> + */ + +#include <linux/bitfield.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/types.h> + +#include "ipmi_si.h" + +#define LS2K_KCS_FIFO_IBFH 0x0 +#define LS2K_KCS_FIFO_IBFT 0x1 +#define LS2K_KCS_FIFO_OBFH 0x2 +#define LS2K_KCS_FIFO_OBFT 0x3 + +/* KCS registers */ +#define LS2K_KCS_REG_STS 0x4 +#define LS2K_KCS_REG_DATA_OUT 0x5 +#define LS2K_KCS_REG_DATA_IN 0x6 +#define LS2K_KCS_REG_CMD 0x8 + +#define LS2K_KCS_CMD_DATA 0xa +#define LS2K_KCS_VERSION 0xb +#define LS2K_KCS_WR_REQ 0xc +#define LS2K_KCS_WR_ACK 0x10 + +#define LS2K_KCS_STS_OBF BIT(0) +#define LS2K_KCS_STS_IBF BIT(1) +#define LS2K_KCS_STS_SMS_ATN BIT(2) +#define LS2K_KCS_STS_CMD BIT(3) + +#define LS2K_KCS_DATA_MASK (LS2K_KCS_STS_OBF | LS2K_KCS_STS_IBF | LS2K_KCS_STS_CMD) + +static bool ls2k_registered; + +static unsigned char ls2k_mem_inb_v0(const struct si_sm_io *io, unsigned int offset) +{ + void __iomem *addr = io->addr; + int reg_offset; + + if (offset & BIT(0)) { + reg_offset = LS2K_KCS_REG_STS; + } else { + writeb(readb(addr + LS2K_KCS_REG_STS) & ~LS2K_KCS_STS_OBF, addr + LS2K_KCS_REG_STS); + reg_offset = LS2K_KCS_REG_DATA_OUT; + } + + return readb(addr + reg_offset); +} + +static unsigned char ls2k_mem_inb_v1(const struct si_sm_io *io, unsigned int offset) +{ + void __iomem *addr = io->addr; + unsigned char inb = 0, cmd; + bool obf, ibf; + + obf = readb(addr + LS2K_KCS_FIFO_OBFH) ^ readb(addr + LS2K_KCS_FIFO_OBFT); + ibf = readb(addr + LS2K_KCS_FIFO_IBFH) ^ readb(addr + LS2K_KCS_FIFO_IBFT); + cmd = readb(addr + LS2K_KCS_CMD_DATA); + + if (offset & BIT(0)) { + inb = readb(addr + LS2K_KCS_REG_STS) & ~LS2K_KCS_DATA_MASK; + inb |= FIELD_PREP(LS2K_KCS_STS_OBF, obf) + | FIELD_PREP(LS2K_KCS_STS_IBF, ibf) + | FIELD_PREP(LS2K_KCS_STS_CMD, cmd); + } else { + inb = readb(addr + LS2K_KCS_REG_DATA_OUT); + writeb(readb(addr + LS2K_KCS_FIFO_OBFH), addr + LS2K_KCS_FIFO_OBFT); + } + + return inb; +} + +static void ls2k_mem_outb_v0(const struct si_sm_io *io, unsigned int offset, + unsigned char val) +{ + void __iomem *addr = io->addr; + unsigned char sts = readb(addr + LS2K_KCS_REG_STS); + int reg_offset; + + if (sts & LS2K_KCS_STS_IBF) + return; + + if (offset & BIT(0)) { + reg_offset = LS2K_KCS_REG_CMD; + sts |= LS2K_KCS_STS_CMD; + } else { + reg_offset = LS2K_KCS_REG_DATA_IN; + sts &= ~LS2K_KCS_STS_CMD; + } + + writew(val, addr + reg_offset); + writeb(sts | LS2K_KCS_STS_IBF, addr + LS2K_KCS_REG_STS); + writel(readl(addr + LS2K_KCS_WR_REQ) + 1, addr + LS2K_KCS_WR_REQ); +} + +static void ls2k_mem_outb_v1(const struct si_sm_io *io, unsigned int offset, + unsigned char val) +{ + void __iomem *addr = io->addr; + unsigned char ibfh, ibft; + int reg_offset; + + ibfh = readb(addr + LS2K_KCS_FIFO_IBFH); + ibft = readb(addr + LS2K_KCS_FIFO_IBFT); + + if (ibfh ^ ibft) + return; + + reg_offset = (offset & BIT(0)) ? LS2K_KCS_REG_CMD : LS2K_KCS_REG_DATA_IN; + writew(val, addr + reg_offset); + + writeb(offset & BIT(0), addr + LS2K_KCS_CMD_DATA); + writeb(!ibft, addr + LS2K_KCS_FIFO_IBFH); + writel(readl(addr + LS2K_KCS_WR_REQ) + 1, addr + LS2K_KCS_WR_REQ); +} + +static void ls2k_mem_cleanup(struct si_sm_io *io) +{ + if (io->addr) + iounmap(io->addr); +} + +static int ipmi_ls2k_mem_setup(struct si_sm_io *io) +{ + unsigned char version; + + io->addr = ioremap(io->addr_data, io->regspacing); + if (!io->addr) + return -EIO; + + version = readb(io->addr + LS2K_KCS_VERSION); + + io->inputb = version ? ls2k_mem_inb_v1 : ls2k_mem_inb_v0; + io->outputb = version ? ls2k_mem_outb_v1 : ls2k_mem_outb_v0; + io->io_cleanup = ls2k_mem_cleanup; + + return 0; +} + +static int ipmi_ls2k_probe(struct platform_device *pdev) +{ + struct si_sm_io io; + + memset(&io, 0, sizeof(io)); + + io.si_info = &ipmi_kcs_si_info; + io.io_setup = ipmi_ls2k_mem_setup; + io.addr_data = pdev->resource[0].start; + io.regspacing = resource_size(&pdev->resource[0]); + io.dev = &pdev->dev; + + dev_dbg(&pdev->dev, "addr 0x%lx, spacing %d.\n", io.addr_data, io.regspacing); + + return ipmi_si_add_smi(&io); +} + +static void ipmi_ls2k_remove(struct platform_device *pdev) +{ + ipmi_si_remove_by_dev(&pdev->dev); +} + +static struct platform_driver ipmi_ls2k_platform_driver = { + .driver = { + .name = "ls2k-ipmi-si", + }, + .probe = ipmi_ls2k_probe, + .remove = ipmi_ls2k_remove, +}; + +void ipmi_si_ls2k_init(void) +{ + platform_driver_register(&ipmi_ls2k_platform_driver); + ls2k_registered = true; +} + +void ipmi_si_ls2k_shutdown(void) +{ + if (ls2k_registered) + platform_driver_unregister(&ipmi_ls2k_platform_driver); +} diff --git a/drivers/char/ipmi/ipmi_si_parisc.c b/drivers/char/ipmi/ipmi_si_parisc.c index 2be2967f6b5f..3b0a70d9adbb 100644 --- a/drivers/char/ipmi/ipmi_si_parisc.c +++ b/drivers/char/ipmi/ipmi_si_parisc.c @@ -13,7 +13,7 @@ static int __init ipmi_parisc_probe(struct parisc_device *dev) memset(&io, 0, sizeof(io)); - io.si_type = SI_KCS; + io.si_info = &ipmi_kcs_si_info; io.addr_source = SI_DEVICETREE; io.addr_space = IPMI_MEM_ADDR_SPACE; io.addr_data = dev->hpa.start; diff --git a/drivers/char/ipmi/ipmi_si_pci.c b/drivers/char/ipmi/ipmi_si_pci.c index 8c0ea637aba0..17f72763322d 100644 --- a/drivers/char/ipmi/ipmi_si_pci.c +++ b/drivers/char/ipmi/ipmi_si_pci.c @@ -23,30 +23,32 @@ MODULE_PARM_DESC(trypci, static int ipmi_pci_probe_regspacing(struct si_sm_io *io) { - if (io->si_type == SI_KCS) { - unsigned char status; - int regspacing; - - io->regsize = DEFAULT_REGSIZE; - io->regshift = 0; - - /* detect 1, 4, 16byte spacing */ - for (regspacing = DEFAULT_REGSPACING; regspacing <= 16;) { - io->regspacing = regspacing; - if (io->io_setup(io)) { - dev_err(io->dev, "Could not setup I/O space\n"); - return DEFAULT_REGSPACING; - } - /* write invalid cmd */ - io->outputb(io, 1, 0x10); - /* read status back */ - status = io->inputb(io, 1); - io->io_cleanup(io); - if (status) - return regspacing; - regspacing *= 4; + unsigned char status; + int regspacing; + + if (io->si_info->type != SI_KCS) + return DEFAULT_REGSPACING; + + io->regsize = DEFAULT_REGSIZE; + io->regshift = 0; + + /* detect 1, 4, 16byte spacing */ + for (regspacing = DEFAULT_REGSPACING; regspacing <= 16;) { + io->regspacing = regspacing; + if (io->io_setup(io)) { + dev_err(io->dev, "Could not setup I/O space\n"); + return DEFAULT_REGSPACING; } + /* write invalid cmd */ + io->outputb(io, 1, 0x10); + /* read status back */ + status = io->inputb(io, 1); + io->io_cleanup(io); + if (status) + return regspacing; + regspacing *= 4; } + return DEFAULT_REGSPACING; } @@ -74,15 +76,15 @@ static int ipmi_pci_probe(struct pci_dev *pdev, switch (pdev->class) { case PCI_CLASS_SERIAL_IPMI_SMIC: - io.si_type = SI_SMIC; + io.si_info = &ipmi_smic_si_info; break; case PCI_CLASS_SERIAL_IPMI_KCS: - io.si_type = SI_KCS; + io.si_info = &ipmi_kcs_si_info; break; case PCI_CLASS_SERIAL_IPMI_BT: - io.si_type = SI_BT; + io.si_info = &ipmi_bt_si_info; break; default: diff --git a/drivers/char/ipmi/ipmi_si_platform.c b/drivers/char/ipmi/ipmi_si_platform.c index 550cabd43ae6..bdc481ce1302 100644 --- a/drivers/char/ipmi/ipmi_si_platform.c +++ b/drivers/char/ipmi/ipmi_si_platform.c @@ -163,9 +163,13 @@ static int platform_ipmi_probe(struct platform_device *pdev) switch (type) { case SI_KCS: + io.si_info = &ipmi_kcs_si_info; + break; case SI_SMIC: + io.si_info = &ipmi_smic_si_info; + break; case SI_BT: - io.si_type = type; + io.si_info = &ipmi_bt_si_info; break; case SI_TYPE_INVALID: /* User disabled this in hardcode. */ return -ENODEV; @@ -213,13 +217,10 @@ static int platform_ipmi_probe(struct platform_device *pdev) #ifdef CONFIG_OF static const struct of_device_id of_ipmi_match[] = { - { .type = "ipmi", .compatible = "ipmi-kcs", - .data = (void *)(unsigned long) SI_KCS }, - { .type = "ipmi", .compatible = "ipmi-smic", - .data = (void *)(unsigned long) SI_SMIC }, - { .type = "ipmi", .compatible = "ipmi-bt", - .data = (void *)(unsigned long) SI_BT }, - {}, + { .type = "ipmi", .compatible = "ipmi-kcs", .data = &ipmi_kcs_si_info }, + { .type = "ipmi", .compatible = "ipmi-smic", .data = &ipmi_smic_si_info }, + { .type = "ipmi", .compatible = "ipmi-bt", .data = &ipmi_bt_si_info }, + {} }; MODULE_DEVICE_TABLE(of, of_ipmi_match); @@ -265,7 +266,7 @@ static int of_ipmi_probe(struct platform_device *pdev) } memset(&io, 0, sizeof(io)); - io.si_type = (enum si_type)device_get_match_data(&pdev->dev); + io.si_info = device_get_match_data(&pdev->dev); io.addr_source = SI_DEVICETREE; io.irq_setup = ipmi_std_irq_setup; @@ -275,7 +276,10 @@ static int of_ipmi_probe(struct platform_device *pdev) io.regspacing = regspacing ? be32_to_cpup(regspacing) : DEFAULT_REGSPACING; io.regshift = regshift ? be32_to_cpup(regshift) : 0; - io.irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + io.irq = platform_get_irq_optional(pdev, 0); + if (io.irq < 0) + io.irq = 0; + io.dev = &pdev->dev; dev_dbg(&pdev->dev, "addr 0x%lx regsize %d spacing %d irq %d\n", @@ -296,7 +300,7 @@ static int find_slave_address(struct si_sm_io *io, int slave_addr) { #ifdef CONFIG_IPMI_DMI_DECODE if (!slave_addr) - slave_addr = ipmi_dmi_get_slave_addr(io->si_type, + slave_addr = ipmi_dmi_get_slave_addr(io->si_info->type, io->addr_space, io->addr_data); #endif @@ -335,13 +339,13 @@ static int acpi_ipmi_probe(struct platform_device *pdev) switch (tmp) { case 1: - io.si_type = SI_KCS; + io.si_info = &ipmi_kcs_si_info; break; case 2: - io.si_type = SI_SMIC; + io.si_info = &ipmi_smic_si_info; break; case 3: - io.si_type = SI_BT; + io.si_info = &ipmi_bt_si_info; break; case 4: /* SSIF, just ignore */ return -ENODEV; @@ -432,9 +436,9 @@ void ipmi_remove_platform_device_by_name(char *name) } static const struct platform_device_id si_plat_ids[] = { - { "dmi-ipmi-si", 0 }, - { "hardcode-ipmi-si", 0 }, - { "hotmod-ipmi-si", 0 }, + { .name = "dmi-ipmi-si" }, + { .name = "hardcode-ipmi-si" }, + { .name = "hotmod-ipmi-si" }, { } }; diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index 506d9988721e..07f1d2327bb7 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -225,6 +225,9 @@ struct ssif_info { bool has_event_buffer; bool supports_alert; + /* When requesting events and messages, don't do it forever. */ + unsigned int num_requests_in_a_row; + /* * Used to tell what we should do with alerts. If we are * waiting on a response, read the data immediately. @@ -413,7 +416,10 @@ static void start_event_fetch(struct ssif_info *ssif_info, unsigned long *flags) } ssif_info->curr_msg = msg; - ssif_info->ssif_state = SSIF_GETTING_EVENTS; + if (ssif_info->ssif_state != SSIF_GETTING_EVENTS) { + ssif_info->num_requests_in_a_row = 0; + ssif_info->ssif_state = SSIF_GETTING_EVENTS; + } ipmi_ssif_unlock_cond(ssif_info, flags); msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); @@ -436,7 +442,10 @@ static void start_recv_msg_fetch(struct ssif_info *ssif_info, } ssif_info->curr_msg = msg; - ssif_info->ssif_state = SSIF_GETTING_MESSAGES; + if (ssif_info->ssif_state != SSIF_GETTING_MESSAGES) { + ssif_info->num_requests_in_a_row = 0; + ssif_info->ssif_state = SSIF_GETTING_MESSAGES; + } ipmi_ssif_unlock_cond(ssif_info, flags); msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); @@ -481,8 +490,6 @@ static int ipmi_ssif_thread(void *data) /* Wait for something to do */ result = wait_for_completion_interruptible( &ssif_info->wake_thread); - if (ssif_info->stopping) - break; if (result == -ERESTARTSYS) continue; init_completion(&ssif_info->wake_thread); @@ -541,7 +548,8 @@ static void start_resend(struct ssif_info *ssif_info); static void retry_timeout(struct timer_list *t) { - struct ssif_info *ssif_info = from_timer(ssif_info, t, retry_timer); + struct ssif_info *ssif_info = timer_container_of(ssif_info, t, + retry_timer); unsigned long oflags, *flags; bool waiting, resend; @@ -565,7 +573,8 @@ static void retry_timeout(struct timer_list *t) static void watch_timeout(struct timer_list *t) { - struct ssif_info *ssif_info = from_timer(ssif_info, t, watch_timer); + struct ssif_info *ssif_info = timer_container_of(ssif_info, t, + watch_timer); unsigned long oflags, *flags; if (ssif_info->stopping) @@ -599,7 +608,7 @@ static void ssif_alert(struct i2c_client *client, enum i2c_alert_protocol type, flags = ipmi_ssif_lock_cond(ssif_info, &oflags); if (ssif_info->waiting_alert) { ssif_info->waiting_alert = false; - del_timer(&ssif_info->retry_timer); + timer_delete(&ssif_info->retry_timer); do_get = true; } else if (ssif_info->curr_msg) { ssif_info->got_alert = true; @@ -843,6 +852,11 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, ssif_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL; handle_flags(ssif_info, flags); } else { + ssif_info->num_requests_in_a_row++; + if (ssif_info->num_requests_in_a_row > 10) + /* Stop if we do this too many times. */ + ssif_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL; + handle_flags(ssif_info, flags); ssif_inc_stat(ssif_info, events); deliver_recv_msg(ssif_info, msg); @@ -876,6 +890,11 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, ssif_info->msg_flags &= ~RECEIVE_MSG_AVAIL; handle_flags(ssif_info, flags); } else { + ssif_info->num_requests_in_a_row++; + if (ssif_info->num_requests_in_a_row > 10) + /* Stop if we do this too many times. */ + ssif_info->msg_flags &= ~RECEIVE_MSG_AVAIL; + ssif_inc_stat(ssif_info, incoming_messages); handle_flags(ssif_info, flags); deliver_recv_msg(ssif_info, msg); @@ -1068,8 +1087,7 @@ static void start_next_msg(struct ssif_info *ssif_info, unsigned long *flags) } } -static void sender(void *send_info, - struct ipmi_smi_msg *msg) +static int sender(void *send_info, struct ipmi_smi_msg *msg) { struct ssif_info *ssif_info = send_info; unsigned long oflags, *flags; @@ -1084,11 +1102,10 @@ static void sender(void *send_info, struct timespec64 t; ktime_get_real_ts64(&t); - dev_dbg(&ssif_info->client->dev, - "**Enqueue %02x %02x: %lld.%6.6ld\n", - msg->data[0], msg->data[1], - (long long)t.tv_sec, (long)t.tv_nsec / NSEC_PER_USEC); + dev_dbg(&ssif_info->client->dev, "**Enqueue %02x %02x: %ptSp\n", + msg->data[0], msg->data[1], &t); } + return IPMI_CC_NO_ERROR; } static int get_smi_info(void *send_info, struct ipmi_smi_info *data) @@ -1268,11 +1285,11 @@ static void shutdown_ssif(void *send_info) schedule_timeout(1); ssif_info->stopping = true; - del_timer_sync(&ssif_info->watch_timer); - del_timer_sync(&ssif_info->retry_timer); + timer_delete_sync(&ssif_info->watch_timer); + timer_delete_sync(&ssif_info->retry_timer); if (ssif_info->thread) { - complete(&ssif_info->wake_thread); kthread_stop(ssif_info->thread); + ssif_info->thread = NULL; } } @@ -1606,7 +1623,7 @@ static int ssif_add_infos(struct i2c_client *client) { struct ssif_addr_info *info; - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = kzalloc_obj(*info); if (!info) return -ENOMEM; info->addr_src = SI_ACPI; @@ -1662,6 +1679,7 @@ static int ssif_probe(struct i2c_client *client) int len = 0; int i; u8 slave_addr = 0; + unsigned int thread_num; struct ssif_addr_info *addr_info = NULL; mutex_lock(&ssif_infos_mutex); @@ -1671,7 +1689,7 @@ static int ssif_probe(struct i2c_client *client) return -ENOMEM; } - ssif_info = kzalloc(sizeof(*ssif_info), GFP_KERNEL); + ssif_info = kzalloc_obj(*ssif_info); if (!ssif_info) { kfree(resp); mutex_unlock(&ssif_infos_mutex); @@ -1880,22 +1898,18 @@ static int ssif_probe(struct i2c_client *client) ssif_info->handlers.request_events = request_events; ssif_info->handlers.set_need_watch = ssif_set_need_watch; - { - unsigned int thread_num; - - thread_num = ((i2c_adapter_id(ssif_info->client->adapter) - << 8) | - ssif_info->client->addr); - init_completion(&ssif_info->wake_thread); - ssif_info->thread = kthread_run(ipmi_ssif_thread, ssif_info, - "kssif%4.4x", thread_num); - if (IS_ERR(ssif_info->thread)) { - rv = PTR_ERR(ssif_info->thread); - dev_notice(&ssif_info->client->dev, - "Could not start kernel thread: error %d\n", - rv); - goto out; - } + thread_num = ((i2c_adapter_id(ssif_info->client->adapter) << 8) | + ssif_info->client->addr); + init_completion(&ssif_info->wake_thread); + ssif_info->thread = kthread_run(ipmi_ssif_thread, ssif_info, + "kssif%4.4x", thread_num); + if (IS_ERR(ssif_info->thread)) { + rv = PTR_ERR(ssif_info->thread); + ssif_info->thread = NULL; + dev_notice(&ssif_info->client->dev, + "Could not start kernel thread: error %d\n", + rv); + goto out; } dev_set_drvdata(&ssif_info->client->dev, ssif_info); @@ -1920,6 +1934,15 @@ static int ssif_probe(struct i2c_client *client) out: if (rv) { + /* + * If ipmi_register_smi() starts the interface, it will + * call shutdown and that will free the thread and set + * it to NULL. Otherwise it must be freed here. + */ + if (ssif_info->thread) { + kthread_stop(ssif_info->thread); + ssif_info->thread = NULL; + } if (addr_info) addr_info->client = NULL; @@ -1952,7 +1975,7 @@ static int new_ssif_client(int addr, char *adapter_name, goto out_unlock; } - addr_info = kzalloc(sizeof(*addr_info), GFP_KERNEL); + addr_info = kzalloc_obj(*addr_info); if (!addr_info) { rv = -ENOMEM; goto out_unlock; @@ -2071,7 +2094,7 @@ static int dmi_ipmi_probe(struct platform_device *pdev) #endif static const struct i2c_device_id ssif_id[] = { - { DEVICE_NAME }, + { .name = DEVICE_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, ssif_id); @@ -2104,7 +2127,7 @@ static void ssif_platform_remove(struct platform_device *dev) } static const struct platform_device_id ssif_plat_ids[] = { - { "dmi-ipmi-ssif", 0 }, + { .name = "dmi-ipmi-ssif" }, { } }; MODULE_DEVICE_TABLE(platform, ssif_plat_ids); diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index f1875b2bebbc..a013ddbf1466 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -150,7 +150,7 @@ static char preaction[16] = "pre_none"; static unsigned char preop_val = WDOG_PREOP_NONE; static char preop[16] = "preop_none"; -static DEFINE_SPINLOCK(ipmi_read_lock); +static DEFINE_MUTEX(ipmi_read_mutex); static char data_to_read; static DECLARE_WAIT_QUEUE_HEAD(read_q); static struct fasync_struct *fasync_q; @@ -363,7 +363,7 @@ static int __ipmi_set_timeout(struct ipmi_smi_msg *smi_msg, { struct kernel_ipmi_msg msg; unsigned char data[6]; - int rv; + int rv = 0; struct ipmi_system_interface_addr addr; int hbnow = 0; @@ -405,14 +405,18 @@ static int __ipmi_set_timeout(struct ipmi_smi_msg *smi_msg, msg.cmd = IPMI_WDOG_SET_TIMER; msg.data = data; msg.data_len = sizeof(data); - rv = ipmi_request_supply_msgs(watchdog_user, - (struct ipmi_addr *) &addr, - 0, - &msg, - NULL, - smi_msg, - recv_msg, - 1); + if (smi_msg) + rv = ipmi_request_supply_msgs(watchdog_user, + (struct ipmi_addr *) &addr, + 0, + &msg, + NULL, + smi_msg, + recv_msg, + 1); + else + ipmi_panic_request_and_wait(watchdog_user, + (struct ipmi_addr *) &addr, &msg); if (rv) pr_warn("set timeout error: %d\n", rv); else if (send_heartbeat_now) @@ -431,9 +435,7 @@ static int _ipmi_set_timeout(int do_heartbeat) atomic_set(&msg_tofree, 2); - rv = __ipmi_set_timeout(&smi_msg, - &recv_msg, - &send_heartbeat_now); + rv = __ipmi_set_timeout(&smi_msg, &recv_msg, &send_heartbeat_now); if (rv) { atomic_set(&msg_tofree, 0); return rv; @@ -460,27 +462,10 @@ static int ipmi_set_timeout(int do_heartbeat) return rv; } -static atomic_t panic_done_count = ATOMIC_INIT(0); - -static void panic_smi_free(struct ipmi_smi_msg *msg) -{ - atomic_dec(&panic_done_count); -} -static void panic_recv_free(struct ipmi_recv_msg *msg) -{ - atomic_dec(&panic_done_count); -} - -static struct ipmi_smi_msg panic_halt_heartbeat_smi_msg = - INIT_IPMI_SMI_MSG(panic_smi_free); -static struct ipmi_recv_msg panic_halt_heartbeat_recv_msg = - INIT_IPMI_RECV_MSG(panic_recv_free); - static void panic_halt_ipmi_heartbeat(void) { struct kernel_ipmi_msg msg; struct ipmi_system_interface_addr addr; - int rv; /* * Don't reset the timer if we have the timer turned off, that @@ -497,24 +482,10 @@ static void panic_halt_ipmi_heartbeat(void) msg.cmd = IPMI_WDOG_RESET_TIMER; msg.data = NULL; msg.data_len = 0; - atomic_add(2, &panic_done_count); - rv = ipmi_request_supply_msgs(watchdog_user, - (struct ipmi_addr *) &addr, - 0, - &msg, - NULL, - &panic_halt_heartbeat_smi_msg, - &panic_halt_heartbeat_recv_msg, - 1); - if (rv) - atomic_sub(2, &panic_done_count); + ipmi_panic_request_and_wait(watchdog_user, (struct ipmi_addr *) &addr, + &msg); } -static struct ipmi_smi_msg panic_halt_smi_msg = - INIT_IPMI_SMI_MSG(panic_smi_free); -static struct ipmi_recv_msg panic_halt_recv_msg = - INIT_IPMI_RECV_MSG(panic_recv_free); - /* * Special call, doesn't claim any locks. This is only to be called * at panic or halt time, in run-to-completion mode, when the caller @@ -526,22 +497,13 @@ static void panic_halt_ipmi_set_timeout(void) int send_heartbeat_now; int rv; - /* Wait for the messages to be free. */ - while (atomic_read(&panic_done_count) != 0) - ipmi_poll_interface(watchdog_user); - atomic_add(2, &panic_done_count); - rv = __ipmi_set_timeout(&panic_halt_smi_msg, - &panic_halt_recv_msg, - &send_heartbeat_now); + rv = __ipmi_set_timeout(NULL, NULL, &send_heartbeat_now); if (rv) { - atomic_sub(2, &panic_done_count); pr_warn("Unable to extend the watchdog timeout\n"); } else { if (send_heartbeat_now) panic_halt_ipmi_heartbeat(); } - while (atomic_read(&panic_done_count) != 0) - ipmi_poll_interface(watchdog_user); } static int __ipmi_heartbeat(void) @@ -793,7 +755,7 @@ static ssize_t ipmi_read(struct file *file, * Reading returns if the pretimeout has gone off, and it only does * it once per pretimeout. */ - spin_lock_irq(&ipmi_read_lock); + mutex_lock(&ipmi_read_mutex); if (!data_to_read) { if (file->f_flags & O_NONBLOCK) { rv = -EAGAIN; @@ -804,9 +766,9 @@ static ssize_t ipmi_read(struct file *file, add_wait_queue(&read_q, &wait); while (!data_to_read && !signal_pending(current)) { set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irq(&ipmi_read_lock); + mutex_unlock(&ipmi_read_mutex); schedule(); - spin_lock_irq(&ipmi_read_lock); + mutex_lock(&ipmi_read_mutex); } remove_wait_queue(&read_q, &wait); @@ -818,7 +780,7 @@ static ssize_t ipmi_read(struct file *file, data_to_read = 0; out: - spin_unlock_irq(&ipmi_read_lock); + mutex_unlock(&ipmi_read_mutex); if (rv == 0) { if (copy_to_user(buf, &data_to_read, 1)) @@ -856,10 +818,10 @@ static __poll_t ipmi_poll(struct file *file, poll_table *wait) poll_wait(file, &read_q, wait); - spin_lock_irq(&ipmi_read_lock); + mutex_lock(&ipmi_read_mutex); if (data_to_read) mask |= (EPOLLIN | EPOLLRDNORM); - spin_unlock_irq(&ipmi_read_lock); + mutex_unlock(&ipmi_read_mutex); return mask; } @@ -932,13 +894,11 @@ static void ipmi_wdog_pretimeout_handler(void *handler_data) if (atomic_inc_and_test(&preop_panic_excl)) panic("Watchdog pre-timeout"); } else if (preop_val == WDOG_PREOP_GIVE_DATA) { - unsigned long flags; - - spin_lock_irqsave(&ipmi_read_lock, flags); + mutex_lock(&ipmi_read_mutex); data_to_read = 1; wake_up_interruptible(&read_q); kill_fasync(&fasync_q, SIGIO, POLL_IN); - spin_unlock_irqrestore(&ipmi_read_lock, flags); + mutex_unlock(&ipmi_read_mutex); } } @@ -1186,14 +1146,8 @@ static struct ipmi_smi_watcher smi_watcher = { .smi_gone = ipmi_smi_gone }; -static int action_op(const char *inval, char *outval) +static int action_op_set_val(const char *inval) { - if (outval) - strcpy(outval, action); - - if (!inval) - return 0; - if (strcmp(inval, "reset") == 0) action_val = WDOG_TIMEOUT_RESET; else if (strcmp(inval, "none") == 0) @@ -1204,18 +1158,26 @@ static int action_op(const char *inval, char *outval) action_val = WDOG_TIMEOUT_POWER_DOWN; else return -EINVAL; - strcpy(action, inval); return 0; } -static int preaction_op(const char *inval, char *outval) +static int action_op(const char *inval, char *outval) { + int rv; + if (outval) - strcpy(outval, preaction); + strcpy(outval, action); if (!inval) return 0; + rv = action_op_set_val(inval); + if (!rv) + strcpy(action, inval); + return rv; +} +static int preaction_op_set_val(const char *inval) +{ if (strcmp(inval, "pre_none") == 0) preaction_val = WDOG_PRETIMEOUT_NONE; else if (strcmp(inval, "pre_smi") == 0) @@ -1228,18 +1190,26 @@ static int preaction_op(const char *inval, char *outval) preaction_val = WDOG_PRETIMEOUT_MSG_INT; else return -EINVAL; - strcpy(preaction, inval); return 0; } -static int preop_op(const char *inval, char *outval) +static int preaction_op(const char *inval, char *outval) { + int rv; + if (outval) - strcpy(outval, preop); + strcpy(outval, preaction); if (!inval) return 0; + rv = preaction_op_set_val(inval); + if (!rv) + strcpy(preaction, inval); + return 0; +} +static int preop_op_set_val(const char *inval) +{ if (strcmp(inval, "preop_none") == 0) preop_val = WDOG_PREOP_NONE; else if (strcmp(inval, "preop_panic") == 0) @@ -1248,7 +1218,22 @@ static int preop_op(const char *inval, char *outval) preop_val = WDOG_PREOP_GIVE_DATA; else return -EINVAL; - strcpy(preop, inval); + return 0; +} + +static int preop_op(const char *inval, char *outval) +{ + int rv; + + if (outval) + strcpy(outval, preop); + + if (!inval) + return 0; + + rv = preop_op_set_val(inval); + if (!rv) + strcpy(preop, inval); return 0; } @@ -1285,18 +1270,18 @@ static int __init ipmi_wdog_init(void) { int rv; - if (action_op(action, NULL)) { + if (action_op_set_val(action)) { action_op("reset", NULL); pr_info("Unknown action '%s', defaulting to reset\n", action); } - if (preaction_op(preaction, NULL)) { + if (preaction_op_set_val(preaction)) { preaction_op("pre_none", NULL); pr_info("Unknown preaction '%s', defaulting to none\n", preaction); } - if (preop_op(preop, NULL)) { + if (preop_op_set_val(preop)) { preop_op("preop_none", NULL); pr_info("Unknown preop '%s', defaulting to none\n", preop); } diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c index c03bc1ec593a..a13a3470c17a 100644 --- a/drivers/char/ipmi/kcs_bmc_aspeed.c +++ b/drivers/char/ipmi/kcs_bmc_aspeed.c @@ -428,7 +428,7 @@ static void aspeed_kcs_irq_mask_update(struct kcs_bmc_device *kcs_bmc, u8 mask, if (rc == -ETIMEDOUT) mod_timer(&priv->obe.timer, jiffies + OBE_POLL_PERIOD); } else { - del_timer(&priv->obe.timer); + timer_delete(&priv->obe.timer); } } @@ -655,7 +655,7 @@ static void aspeed_kcs_remove(struct platform_device *pdev) spin_lock_irq(&priv->obe.lock); priv->obe.remove = true; spin_unlock_irq(&priv->obe.lock); - del_timer_sync(&priv->obe.timer); + timer_delete_sync(&priv->obe.timer); } static const struct of_device_id ast_kcs_bmc_match[] = { diff --git a/drivers/char/ipmi/kcs_bmc_serio.c b/drivers/char/ipmi/kcs_bmc_serio.c index 1793358be782..1a95d6f258ea 100644 --- a/drivers/char/ipmi/kcs_bmc_serio.c +++ b/drivers/char/ipmi/kcs_bmc_serio.c @@ -77,7 +77,7 @@ static int kcs_bmc_serio_add_device(struct kcs_bmc_device *kcs_bmc) return -ENOMEM; /* Use kzalloc() as the allocation is cleaned up with kfree() via serio_unregister_port() */ - port = kzalloc(sizeof(*port), GFP_KERNEL); + port = kzalloc_obj(*port); if (!port) return -ENOMEM; diff --git a/drivers/char/ipmi/ssif_bmc.c b/drivers/char/ipmi/ssif_bmc.c index 310f17dd9511..6036897725f3 100644 --- a/drivers/char/ipmi/ssif_bmc.c +++ b/drivers/char/ipmi/ssif_bmc.c @@ -18,6 +18,9 @@ #include <linux/timer.h> #include <linux/jiffies.h> #include <linux/ipmi_ssif_bmc.h> +#if IS_ENABLED(CONFIG_SSIF_IPMI_BMC_KUNIT_TEST) +#include <kunit/test.h> +#endif #define DEVICE_NAME "ipmi-ssif-host" @@ -163,6 +166,8 @@ static ssize_t ssif_bmc_read(struct file *file, char __user *buf, size_t count, spin_unlock_irqrestore(&ssif_bmc->lock, flags); ret = copy_to_user(buf, &msg, count); + if (ret > 0) + ret = -EFAULT; } return (ret < 0) ? ret : count; @@ -209,7 +214,7 @@ static ssize_t ssif_bmc_write(struct file *file, const char __user *buf, size_t if (ret) goto exit; - del_timer(&ssif_bmc->response_timer); + timer_delete(&ssif_bmc->response_timer); ssif_bmc->response_timer_inited = false; memcpy(&ssif_bmc->response, &msg, count); @@ -297,7 +302,8 @@ static void complete_response(struct ssif_bmc_ctx *ssif_bmc) static void response_timeout(struct timer_list *t) { - struct ssif_bmc_ctx *ssif_bmc = from_timer(ssif_bmc, t, response_timer); + struct ssif_bmc_ctx *ssif_bmc = timer_container_of(ssif_bmc, t, + response_timer); unsigned long flags; spin_lock_irqsave(&ssif_bmc->lock, flags); @@ -455,6 +461,15 @@ static bool supported_write_cmd(u8 cmd) return false; } +static bool supported_write_start_cmd(u8 cmd) +{ + if (cmd == SSIF_IPMI_SINGLEPART_WRITE || + cmd == SSIF_IPMI_MULTIPART_WRITE_START) + return true; + + return false; +} + /* Process the IPMI response that will be read by master */ static void handle_read_processed(struct ssif_bmc_ctx *ssif_bmc, u8 *val) { @@ -557,7 +572,7 @@ static void process_request_part(struct ssif_bmc_ctx *ssif_bmc) len = ssif_bmc->request.len + part->length; /* Do the bound check here, not allow the request len exceed 254 bytes */ if (len > IPMI_SSIF_PAYLOAD_MAX) { - dev_warn(&ssif_bmc->client->dev, + dev_dbg(&ssif_bmc->client->dev, "Warn: Request exceeded 254 bytes, aborting"); /* Request too long, aborting */ ssif_bmc->aborting = true; @@ -603,7 +618,7 @@ static void on_read_requested_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) ssif_bmc->state == SSIF_START || ssif_bmc->state == SSIF_REQ_RECVING || ssif_bmc->state == SSIF_RES_SENDING) { - dev_warn(&ssif_bmc->client->dev, + dev_dbg(&ssif_bmc->client->dev, "Warn: %s unexpected READ REQUESTED in state=%s\n", __func__, state_to_string(ssif_bmc->state)); ssif_bmc->state = SSIF_ABORTING; @@ -612,7 +627,7 @@ static void on_read_requested_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) } else if (ssif_bmc->state == SSIF_SMBUS_CMD) { if (!supported_read_cmd(ssif_bmc->part_buf.smbus_cmd)) { - dev_warn(&ssif_bmc->client->dev, "Warn: Unknown SMBus read command=0x%x", + dev_dbg(&ssif_bmc->client->dev, "Warn: Unknown SMBus read command=0x%x", ssif_bmc->part_buf.smbus_cmd); ssif_bmc->aborting = true; } @@ -647,7 +662,7 @@ static void on_read_processed_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) ssif_bmc->state == SSIF_START || ssif_bmc->state == SSIF_REQ_RECVING || ssif_bmc->state == SSIF_SMBUS_CMD) { - dev_warn(&ssif_bmc->client->dev, + dev_dbg(&ssif_bmc->client->dev, "Warn: %s unexpected READ PROCESSED in state=%s\n", __func__, state_to_string(ssif_bmc->state)); ssif_bmc->state = SSIF_ABORTING; @@ -672,7 +687,7 @@ static void on_write_requested_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) } else if (ssif_bmc->state == SSIF_START || ssif_bmc->state == SSIF_REQ_RECVING || ssif_bmc->state == SSIF_RES_SENDING) { - dev_warn(&ssif_bmc->client->dev, + dev_dbg(&ssif_bmc->client->dev, "Warn: %s unexpected WRITE REQUEST in state=%s\n", __func__, state_to_string(ssif_bmc->state)); ssif_bmc->state = SSIF_ABORTING; @@ -687,7 +702,7 @@ static void on_write_received_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) { if (ssif_bmc->state == SSIF_READY || ssif_bmc->state == SSIF_RES_SENDING) { - dev_warn(&ssif_bmc->client->dev, + dev_dbg(&ssif_bmc->client->dev, "Warn: %s unexpected WRITE RECEIVED in state=%s\n", __func__, state_to_string(ssif_bmc->state)); ssif_bmc->state = SSIF_ABORTING; @@ -697,7 +712,7 @@ static void on_write_received_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) } else if (ssif_bmc->state == SSIF_SMBUS_CMD) { if (!supported_write_cmd(ssif_bmc->part_buf.smbus_cmd)) { - dev_warn(&ssif_bmc->client->dev, "Warn: Unknown SMBus write command=0x%x", + dev_dbg(&ssif_bmc->client->dev, "Warn: Unknown SMBus write command=0x%x", ssif_bmc->part_buf.smbus_cmd); ssif_bmc->aborting = true; } @@ -706,6 +721,11 @@ static void on_write_received_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) ssif_bmc->state = SSIF_ABORTING; else ssif_bmc->state = SSIF_REQ_RECVING; + } else if (ssif_bmc->state == SSIF_ABORTING) { + if (supported_write_start_cmd(*val)) { + ssif_bmc->state = SSIF_SMBUS_CMD; + ssif_bmc->aborting = false; + } } /* This is response sending state */ @@ -721,7 +741,7 @@ static void on_stop_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) ssif_bmc->state == SSIF_START || ssif_bmc->state == SSIF_SMBUS_CMD || ssif_bmc->state == SSIF_ABORTING) { - dev_warn(&ssif_bmc->client->dev, + dev_dbg(&ssif_bmc->client->dev, "Warn: %s unexpected SLAVE STOP in state=%s\n", __func__, state_to_string(ssif_bmc->state)); ssif_bmc->state = SSIF_READY; @@ -788,7 +808,7 @@ static int ssif_bmc_cb(struct i2c_client *client, enum i2c_slave_event event, u8 break; default: - dev_warn(&ssif_bmc->client->dev, "Warn: Unknown i2c slave event\n"); + dev_dbg(&ssif_bmc->client->dev, "Warn: Unknown i2c slave event\n"); break; } @@ -842,6 +862,7 @@ static void ssif_bmc_remove(struct i2c_client *client) { struct ssif_bmc_ctx *ssif_bmc = i2c_get_clientdata(client); + timer_delete_sync(&ssif_bmc->response_timer); i2c_slave_unregister(client); misc_deregister(&ssif_bmc->miscdev); } @@ -853,7 +874,7 @@ static const struct of_device_id ssif_bmc_match[] = { MODULE_DEVICE_TABLE(of, ssif_bmc_match); static const struct i2c_device_id ssif_bmc_id[] = { - { DEVICE_NAME }, + { .name = DEVICE_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, ssif_bmc_id); @@ -868,6 +889,373 @@ static struct i2c_driver ssif_bmc_driver = { .id_table = ssif_bmc_id, }; +#if IS_ENABLED(CONFIG_SSIF_IPMI_BMC_KUNIT_TEST) +struct ssif_bmc_test_ctx { + struct ssif_bmc_ctx ssif_bmc; + struct i2c_client client; + struct i2c_adapter adapter; + struct i2c_algorithm algo; +}; + +static int ssif_bmc_test_init(struct kunit *test) +{ + struct ssif_bmc_test_ctx *test_ctx; + + test_ctx = kunit_kzalloc(test, sizeof(*test_ctx), GFP_KERNEL); + if (!test_ctx) + return -ENOMEM; + + test_ctx->adapter.algo = &test_ctx->algo; + test_ctx->client.addr = 0x20; + test_ctx->client.adapter = &test_ctx->adapter; + + spin_lock_init(&test_ctx->ssif_bmc.lock); + init_waitqueue_head(&test_ctx->ssif_bmc.wait_queue); + test_ctx->ssif_bmc.client = &test_ctx->client; + i2c_set_clientdata(&test_ctx->client, &test_ctx->ssif_bmc); + + test->priv = test_ctx; + + return 0; +} + +static void ssif_bmc_test_exit(struct kunit *test) +{ + struct ssif_bmc_test_ctx *test_ctx = test->priv; + + if (test_ctx->ssif_bmc.response_timer_inited) + timer_delete_sync(&test_ctx->ssif_bmc.response_timer); +} + +static int ssif_bmc_test_run_event_val(struct ssif_bmc_test_ctx *test_ctx, + enum i2c_slave_event event, + u8 *value) +{ + return ssif_bmc_cb(&test_ctx->client, event, value); +} + +static int ssif_bmc_test_run_event(struct ssif_bmc_test_ctx *test_ctx, + enum i2c_slave_event event, u8 value) +{ + return ssif_bmc_test_run_event_val(test_ctx, event, &value); +} + +static void ssif_bmc_test_singlepart_req(struct kunit *test) +{ + struct ssif_bmc_test_ctx *test_ctx = test->priv; + struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc; + + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED, + GET_8BIT_ADDR(test_ctx->client.addr)); + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, + SSIF_IPMI_SINGLEPART_WRITE); + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 2); + + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0xaa); + + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0x55); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), -EBUSY); + + KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_READY); + KUNIT_EXPECT_TRUE(test, ssif_bmc->request_available); + KUNIT_EXPECT_TRUE(test, ssif_bmc->busy); + KUNIT_EXPECT_FALSE(test, ssif_bmc->aborting); + KUNIT_EXPECT_EQ(test, ssif_bmc->request.len, 2); + KUNIT_EXPECT_EQ(test, ssif_bmc->request.payload[0], 0xaa); + KUNIT_EXPECT_EQ(test, ssif_bmc->request.payload[1], 0x55); + KUNIT_EXPECT_TRUE(test, ssif_bmc->response_timer_inited); +} + +static void ssif_bmc_test_restart_write_without_stop(struct kunit *test) +{ + struct ssif_bmc_test_ctx *test_ctx = test->priv; + struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc; + + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED, + GET_8BIT_ADDR(test_ctx->client.addr)), 0); + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, + SSIF_IPMI_SINGLEPART_WRITE), 0); + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 2), 0); + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0xde), 0); + + KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_REQ_RECVING); + + /* Write transaction, without stop, and new request coming */ + ssif_bmc_test_singlepart_req(test); +} + + +static void ssif_bmc_test_restart_after_invalid_command(struct kunit *test) +{ + struct ssif_bmc_test_ctx *test_ctx = test->priv; + struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc; + + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED, + GET_8BIT_ADDR(test_ctx->client.addr)), 0); + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0xff), 0); + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 1), 0); + + KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_ABORTING); + KUNIT_EXPECT_TRUE(test, ssif_bmc->aborting); + + /* After An Invalid Command, expect could handle new request */ + ssif_bmc_test_singlepart_req(test); +} + +static void ssif_bmc_test_singlepart_read_response_completion(struct kunit *test) +{ + struct ssif_bmc_test_ctx *test_ctx = test->priv; + struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc; + u8 value; + + ssif_bmc->state = SSIF_SMBUS_CMD; + ssif_bmc->part_buf.smbus_cmd = SSIF_IPMI_SINGLEPART_READ; + ssif_bmc->response.len = 2; + ssif_bmc->response.payload[0] = 0x11; + ssif_bmc->response.payload[1] = 0x22; + ssif_bmc->response_in_progress = true; + ssif_bmc->is_singlepart_read = true; + ssif_bmc->pec_support = true; + + value = 0; + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_REQUESTED, + &value), 0); + KUNIT_EXPECT_EQ(test, value, 2); + KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_RES_SENDING); + + value = 0; + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_PROCESSED, + &value), 0); + KUNIT_EXPECT_EQ(test, value, 0x11); + + value = 0; + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_PROCESSED, + &value), 0); + KUNIT_EXPECT_EQ(test, value, 0x22); + + value = 0; + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_PROCESSED, + &value), 0); + KUNIT_EXPECT_EQ(test, value, ssif_bmc->part_buf.pec); + + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), 0); + KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_READY); + KUNIT_EXPECT_FALSE(test, ssif_bmc->response_in_progress); + KUNIT_EXPECT_EQ(test, ssif_bmc->response.len, 0); +} + +static void ssif_bmc_test_stop_during_start_discards_partial_request(struct kunit *test) +{ + struct ssif_bmc_test_ctx *test_ctx = test->priv; + struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc; + + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED, + GET_8BIT_ADDR(test_ctx->client.addr)), 0); + KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_START); + + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), 0); + KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_READY); + KUNIT_EXPECT_FALSE(test, ssif_bmc->request_available); + KUNIT_EXPECT_EQ(test, ssif_bmc->msg_idx, 0); + + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED, + GET_8BIT_ADDR(test_ctx->client.addr)), 0); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, + SSIF_IPMI_SINGLEPART_WRITE), 0); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 1), 0); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0x77), 0); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), -EBUSY); + + KUNIT_EXPECT_TRUE(test, ssif_bmc->request_available); + KUNIT_EXPECT_EQ(test, ssif_bmc->request.len, 1); + KUNIT_EXPECT_EQ(test, ssif_bmc->request.payload[0], 0x77); +} + +static void ssif_bmc_test_read_interrupts_partial_write(struct kunit *test) +{ + struct ssif_bmc_test_ctx *test_ctx = test->priv; + struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc; + u8 value = 0xff; + + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED, + GET_8BIT_ADDR(test_ctx->client.addr)), 0); + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, + SSIF_IPMI_SINGLEPART_WRITE), 0); + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 2), 0); + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0xab), 0); + + KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_REQ_RECVING); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_REQUESTED, + &value), 0); + KUNIT_EXPECT_EQ(test, value, 0); + KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_ABORTING); + + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), 0); + KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_READY); + KUNIT_EXPECT_FALSE(test, ssif_bmc->request_available); + KUNIT_EXPECT_EQ(test, ssif_bmc->request.len, 0); + + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED, + GET_8BIT_ADDR(test_ctx->client.addr)), 0); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, + SSIF_IPMI_SINGLEPART_WRITE), 0); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 1), 0); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0xcd), 0); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), -EBUSY); + + KUNIT_EXPECT_TRUE(test, ssif_bmc->request_available); + KUNIT_EXPECT_EQ(test, ssif_bmc->request.len, 1); + KUNIT_EXPECT_EQ(test, ssif_bmc->request.payload[0], 0xcd); +} + +static void ssif_bmc_test_write_interrupts_response_send(struct kunit *test) +{ + struct ssif_bmc_test_ctx *test_ctx = test->priv; + struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc; + u8 value = 0; + + ssif_bmc->state = SSIF_SMBUS_CMD; + ssif_bmc->part_buf.smbus_cmd = SSIF_IPMI_SINGLEPART_READ; + ssif_bmc->response.len = 1; + ssif_bmc->response.payload[0] = 0x66; + ssif_bmc->response_in_progress = true; + ssif_bmc->is_singlepart_read = true; + + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_REQUESTED, + &value), 0); + KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_RES_SENDING); + + /* READ_REQUESTED transaction */ + ssif_bmc_test_singlepart_req(test); +} + +static void ssif_bmc_test_write_interrupts_response_sending(struct kunit *test) +{ + struct ssif_bmc_test_ctx *test_ctx = test->priv; + struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc; + u8 value = 0; + + ssif_bmc->state = SSIF_SMBUS_CMD; + ssif_bmc->part_buf.smbus_cmd = SSIF_IPMI_SINGLEPART_READ; + ssif_bmc->response.len = 1; + ssif_bmc->response.payload[0] = 0x66; + ssif_bmc->response_in_progress = true; + ssif_bmc->is_singlepart_read = true; + + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_REQUESTED, + &value), 0); + KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_RES_SENDING); + + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_PROCESSED, + &value), 0); + KUNIT_EXPECT_EQ(test, value, 0x66); + + /* READ_REQUESTED transaction */ + ssif_bmc_test_singlepart_req(test); +} + +static void ssif_bmc_test_timeout_interrupt_allows_retry(struct kunit *test) +{ + struct ssif_bmc_test_ctx *test_ctx = test->priv; + struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc; + + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED, + GET_8BIT_ADDR(test_ctx->client.addr)), 0); + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, + SSIF_IPMI_SINGLEPART_WRITE), 0); + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 1), 0); + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0x21), 0); + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), -EBUSY); + + KUNIT_ASSERT_TRUE(test, timer_pending(&ssif_bmc->response_timer)); + timer_delete_sync(&ssif_bmc->response_timer); + response_timeout(&ssif_bmc->response_timer); + + KUNIT_EXPECT_FALSE(test, ssif_bmc->busy); + KUNIT_EXPECT_TRUE(test, ssif_bmc->aborting); + KUNIT_EXPECT_FALSE(test, ssif_bmc->response_timer_inited); + + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED, + GET_8BIT_ADDR(test_ctx->client.addr)), 0); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, + SSIF_IPMI_SINGLEPART_WRITE), 0); + KUNIT_EXPECT_FALSE(test, ssif_bmc->aborting); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 1), 0); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0x22), 0); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), -EBUSY); + + KUNIT_EXPECT_TRUE(test, ssif_bmc->request_available); + KUNIT_EXPECT_EQ(test, ssif_bmc->request.len, 1); + KUNIT_EXPECT_EQ(test, ssif_bmc->request.payload[0], 0x22); +} + +static struct kunit_case ssif_bmc_test_cases[] = { + KUNIT_CASE(ssif_bmc_test_singlepart_req), + KUNIT_CASE(ssif_bmc_test_restart_write_without_stop), + KUNIT_CASE(ssif_bmc_test_restart_after_invalid_command), + KUNIT_CASE(ssif_bmc_test_singlepart_read_response_completion), + KUNIT_CASE(ssif_bmc_test_stop_during_start_discards_partial_request), + KUNIT_CASE(ssif_bmc_test_read_interrupts_partial_write), + KUNIT_CASE(ssif_bmc_test_write_interrupts_response_send), + KUNIT_CASE(ssif_bmc_test_write_interrupts_response_sending), + KUNIT_CASE(ssif_bmc_test_timeout_interrupt_allows_retry), + {} +}; + +static struct kunit_suite ssif_bmc_test_suite = { + .name = "ssif_bmc_test", + .init = ssif_bmc_test_init, + .exit = ssif_bmc_test_exit, + .test_cases = ssif_bmc_test_cases, +}; + +kunit_test_suite(ssif_bmc_test_suite); +#endif + module_i2c_driver(ssif_bmc_driver); MODULE_AUTHOR("Quan Nguyen <quan@os.amperecomputing.com>"); diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 169eed162a7f..63253d1de5d7 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -61,29 +61,11 @@ static inline int page_is_allowed(unsigned long pfn) { return devmem_is_allowed(pfn); } -static inline int range_is_allowed(unsigned long pfn, unsigned long size) -{ - u64 from = ((u64)pfn) << PAGE_SHIFT; - u64 to = from + size; - u64 cursor = from; - - while (cursor < to) { - if (!devmem_is_allowed(pfn)) - return 0; - cursor += PAGE_SIZE; - pfn++; - } - return 1; -} #else static inline int page_is_allowed(unsigned long pfn) { return 1; } -static inline int range_is_allowed(unsigned long pfn, unsigned long size) -{ - return 1; -} #endif static inline bool should_stop_iteration(void) @@ -322,13 +304,13 @@ static unsigned zero_mmap_capabilities(struct file *file) } /* can't do an in-place private mapping if there's no MMU */ -static inline int private_mapping_ok(struct vm_area_struct *vma) +static inline int private_mapping_ok(struct vm_area_desc *desc) { - return is_nommu_shared_mapping(vma->vm_flags); + return is_nommu_shared_vma_flags(&desc->vma_flags); } #else -static inline int private_mapping_ok(struct vm_area_struct *vma) +static inline int private_mapping_ok(struct vm_area_desc *desc) { return 1; } @@ -340,46 +322,43 @@ static const struct vm_operations_struct mmap_mem_ops = { #endif }; -static int mmap_mem(struct file *file, struct vm_area_struct *vma) +static int mmap_mem_prepare(struct vm_area_desc *desc) { - size_t size = vma->vm_end - vma->vm_start; - phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT; + struct file *file = desc->file; + const size_t size = vma_desc_size(desc); + const phys_addr_t offset = (phys_addr_t)desc->pgoff << PAGE_SHIFT; /* Does it even fit in phys_addr_t? */ - if (offset >> PAGE_SHIFT != vma->vm_pgoff) + if (offset >> PAGE_SHIFT != desc->pgoff) return -EINVAL; /* It's illegal to wrap around the end of the physical address space. */ if (offset + (phys_addr_t)size - 1 < offset) return -EINVAL; - if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size)) + if (!valid_mmap_phys_addr_range(desc->pgoff, size)) return -EINVAL; - if (!private_mapping_ok(vma)) + if (!private_mapping_ok(desc)) return -ENOSYS; - if (!range_is_allowed(vma->vm_pgoff, size)) + if (!range_is_allowed(desc->pgoff, size)) return -EPERM; - if (!phys_mem_access_prot_allowed(file, vma->vm_pgoff, size, - &vma->vm_page_prot)) + if (!phys_mem_access_prot_allowed(file, desc->pgoff, size, + &desc->page_prot)) return -EINVAL; - vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff, - size, - vma->vm_page_prot); + desc->page_prot = phys_mem_access_prot(file, desc->pgoff, + size, + desc->page_prot); - vma->vm_ops = &mmap_mem_ops; + desc->vm_ops = &mmap_mem_ops; + + /* Remap-pfn-range will mark the range with the I/O flag. */ + mmap_action_remap_full(desc, desc->pgoff); + desc->action.error_override = -EAGAIN; - /* Remap-pfn-range will mark the range VM_IO */ - if (remap_pfn_range(vma, - vma->vm_start, - vma->vm_pgoff, - size, - vma->vm_page_prot)) { - return -EAGAIN; - } return 0; } @@ -519,38 +498,57 @@ static ssize_t read_zero(struct file *file, char __user *buf, return cleared; } -static int mmap_zero(struct file *file, struct vm_area_struct *vma) +static int mmap_zero_prepare(struct vm_area_desc *desc) { #ifndef CONFIG_MMU return -ENOSYS; #endif - if (vma->vm_flags & VM_SHARED) - return shmem_zero_setup(vma); - vma_set_anonymous(vma); + if (vma_desc_test(desc, VMA_SHARED_BIT)) + return shmem_zero_setup_desc(desc); + + /* + * This is a highly unique situation where we mark a MAP_PRIVATE mapping + * of /dev/zero anonymous, despite it not being. + */ + vma_desc_set_anonymous(desc); return 0; } +#ifndef CONFIG_MMU +static unsigned long get_unmapped_area_zero(struct file *file, + unsigned long addr, unsigned long len, + unsigned long pgoff, unsigned long flags) +{ + return -ENOSYS; +} +#else static unsigned long get_unmapped_area_zero(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) { -#ifdef CONFIG_MMU if (flags & MAP_SHARED) { /* - * mmap_zero() will call shmem_zero_setup() to create a file, - * so use shmem's get_unmapped_area in case it can be huge; - * and pass NULL for file as in mmap.c's get_unmapped_area(), - * so as not to confuse shmem with our handle on "/dev/zero". + * mmap_zero_prepare() will call shmem_zero_setup() to create a + * file, so use shmem's get_unmapped_area in case it can be + * huge; and pass NULL for file as in mmap.c's + * get_unmapped_area(), so as not to confuse shmem with our + * handle on "/dev/zero". */ return shmem_get_unmapped_area(NULL, addr, len, pgoff, flags); } - /* Otherwise flags & MAP_PRIVATE: with no shmem object beneath it */ - return mm_get_unmapped_area(current->mm, file, addr, len, pgoff, flags); + /* + * Otherwise flags & MAP_PRIVATE: with no shmem object beneath it, + * attempt to map aligned to huge page size if possible, otherwise we + * fall back to system page size mappings. + */ +#ifdef CONFIG_TRANSPARENT_HUGEPAGE + return thp_get_unmapped_area(file, addr, len, pgoff, flags); #else - return -ENOSYS; + return mm_get_unmapped_area(file, addr, len, pgoff, flags); #endif } +#endif /* CONFIG_MMU */ static ssize_t write_full(struct file *file, const char __user *buf, size_t count, loff_t *ppos) @@ -637,7 +635,7 @@ static const struct file_operations __maybe_unused mem_fops = { .llseek = memory_lseek, .read = read_mem, .write = write_mem, - .mmap = mmap_mem, + .mmap_prepare = mmap_mem_prepare, .open = open_mem, #ifndef CONFIG_MMU .get_unmapped_area = get_unmapped_area_mem, @@ -673,7 +671,7 @@ static const struct file_operations zero_fops = { .write_iter = write_iter_zero, .splice_read = copy_splice_read, .splice_write = splice_write_zero, - .mmap = mmap_zero, + .mmap_prepare = mmap_zero_prepare, .get_unmapped_area = get_unmapped_area_zero, #ifndef CONFIG_MMU .mmap_capabilities = zero_mmap_capabilities, diff --git a/drivers/char/misc.c b/drivers/char/misc.c index f7dd455dd0dd..726516fb0a3b 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -58,9 +58,8 @@ static LIST_HEAD(misc_list); static DEFINE_MUTEX(misc_mtx); /* - * Assigned numbers, used for dynamic minors + * Assigned numbers. */ -#define DYNAMIC_MINORS 128 /* like dynamic majors */ static DEFINE_IDA(misc_minors_ida); static int misc_minor_alloc(int minor) @@ -69,34 +68,17 @@ static int misc_minor_alloc(int minor) if (minor == MISC_DYNAMIC_MINOR) { /* allocate free id */ - ret = ida_alloc_max(&misc_minors_ida, DYNAMIC_MINORS - 1, GFP_KERNEL); - if (ret >= 0) { - ret = DYNAMIC_MINORS - ret - 1; - } else { - ret = ida_alloc_range(&misc_minors_ida, MISC_DYNAMIC_MINOR + 1, - MINORMASK, GFP_KERNEL); - } + ret = ida_alloc_range(&misc_minors_ida, MISC_DYNAMIC_MINOR + 1, + MINORMASK, GFP_KERNEL); } else { - /* specific minor, check if it is in dynamic or misc dynamic range */ - if (minor < DYNAMIC_MINORS) { - minor = DYNAMIC_MINORS - minor - 1; - ret = ida_alloc_range(&misc_minors_ida, minor, minor, GFP_KERNEL); - } else if (minor > MISC_DYNAMIC_MINOR) { - ret = ida_alloc_range(&misc_minors_ida, minor, minor, GFP_KERNEL); - } else { - /* case of non-dynamic minors, no need to allocate id */ - ret = 0; - } + ret = ida_alloc_range(&misc_minors_ida, minor, minor, GFP_KERNEL); } return ret; } static void misc_minor_free(int minor) { - if (minor < DYNAMIC_MINORS) - ida_free(&misc_minors_ida, DYNAMIC_MINORS - minor - 1); - else if (minor > MISC_DYNAMIC_MINOR) - ida_free(&misc_minors_ida, minor); + ida_free(&misc_minors_ida, minor); } #ifdef CONFIG_PROC_FS @@ -150,7 +132,8 @@ static int misc_open(struct inode *inode, struct file *file) break; } - if (!new_fops) { + /* Only request module for fixed minor code */ + if (!new_fops && minor < MISC_DYNAMIC_MINOR) { mutex_unlock(&misc_mtx); request_module("char-major-%d-%d", MISC_MAJOR, minor); mutex_lock(&misc_mtx); @@ -162,10 +145,11 @@ static int misc_open(struct inode *inode, struct file *file) new_fops = fops_get(iter->fops); break; } - if (!new_fops) - goto fail; } + if (!new_fops) + goto fail; + /* * Place the miscdevice in the file's * private_data so it can be used by the @@ -228,6 +212,12 @@ int misc_register(struct miscdevice *misc) int err = 0; bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR); + if (misc->minor > MISC_DYNAMIC_MINOR) { + pr_err("Invalid fixed minor %d for miscdevice '%s'\n", + misc->minor, misc->name); + return -EINVAL; + } + INIT_LIST_HEAD(&misc->list); mutex_lock(&misc_mtx); @@ -293,13 +283,12 @@ EXPORT_SYMBOL(misc_register); void misc_deregister(struct miscdevice *misc) { - if (WARN_ON(list_empty(&misc->list))) - return; - mutex_lock(&misc_mtx); - list_del(&misc->list); + list_del_init(&misc->list); device_destroy(&misc_class, MKDEV(MISC_MAJOR, misc->minor)); misc_minor_free(misc->minor); + if (misc->minor > MISC_DYNAMIC_MINOR) + misc->minor = MISC_DYNAMIC_MINOR; mutex_unlock(&misc_mtx); } EXPORT_SYMBOL(misc_deregister); @@ -307,15 +296,15 @@ EXPORT_SYMBOL(misc_deregister); static int __init misc_init(void) { int err; - struct proc_dir_entry *ret; + struct proc_dir_entry *misc_proc_file; - ret = proc_create_seq("misc", 0, NULL, &misc_seq_ops); + misc_proc_file = proc_create_seq("misc", 0, NULL, &misc_seq_ops); err = class_register(&misc_class); if (err) goto fail_remove; - err = -EIO; - if (register_chrdev(MISC_MAJOR, "misc", &misc_fops)) + err = __register_chrdev(MISC_MAJOR, 0, MINORMASK + 1, "misc", &misc_fops); + if (err < 0) goto fail_printk; return 0; @@ -323,7 +312,7 @@ fail_printk: pr_err("unable to get major %d for misc devices\n", MISC_MAJOR); class_unregister(&misc_class); fail_remove: - if (ret) + if (misc_proc_file) remove_proc_entry("misc", NULL); return err; } diff --git a/drivers/char/misc_minor_kunit.c b/drivers/char/misc_minor_kunit.c new file mode 100644 index 000000000000..e930c78e1ef9 --- /dev/null +++ b/drivers/char/misc_minor_kunit.c @@ -0,0 +1,689 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <kunit/test.h> +#include <kunit/test-bug.h> +#include <linux/module.h> +#include <linux/miscdevice.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/init_syscalls.h> + +/* static minor (LCD_MINOR) */ +static struct miscdevice dev_static_minor = { + .minor = LCD_MINOR, + .name = "dev_static_minor", +}; + +/* misc dynamic minor */ +static struct miscdevice dev_misc_dynamic_minor = { + .minor = MISC_DYNAMIC_MINOR, + .name = "dev_misc_dynamic_minor", +}; + +static void kunit_static_minor(struct kunit *test) +{ + int ret; + + ret = misc_register(&dev_static_minor); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, LCD_MINOR, dev_static_minor.minor); + misc_deregister(&dev_static_minor); +} + +static void kunit_misc_dynamic_minor(struct kunit *test) +{ + int ret; + + ret = misc_register(&dev_misc_dynamic_minor); + KUNIT_EXPECT_EQ(test, 0, ret); + misc_deregister(&dev_misc_dynamic_minor); +} + +struct miscdev_test_case { + const char *str; + int minor; +}; + +static struct miscdev_test_case miscdev_test_ranges[] = { + { + .str = "lower static range, top", + .minor = 15, + }, + { + .str = "upper static range, bottom", + .minor = 130, + }, + { + .str = "lower static range, bottom", + .minor = 0, + }, + { + .str = "upper static range, top", + .minor = MISC_DYNAMIC_MINOR - 1, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(miscdev, miscdev_test_ranges, str); + +static int miscdev_find_minors(struct kunit_suite *suite) +{ + int ret; + struct miscdevice miscstat = { + .name = "miscstat", + }; + int i; + + for (i = 15; i >= 0; i--) { + miscstat.minor = i; + ret = misc_register(&miscstat); + if (ret == 0) + break; + } + + if (ret == 0) { + kunit_info(suite, "found misc device minor %d available\n", + miscstat.minor); + miscdev_test_ranges[0].minor = miscstat.minor; + misc_deregister(&miscstat); + } else { + return ret; + } + + for (i = 128; i < MISC_DYNAMIC_MINOR; i++) { + miscstat.minor = i; + ret = misc_register(&miscstat); + if (ret == 0) + break; + } + + if (ret == 0) { + kunit_info(suite, "found misc device minor %d available\n", + miscstat.minor); + miscdev_test_ranges[1].minor = miscstat.minor; + misc_deregister(&miscstat); + } else { + return ret; + } + + for (i = 0; i < miscdev_test_ranges[0].minor; i++) { + miscstat.minor = i; + ret = misc_register(&miscstat); + if (ret == 0) + break; + } + + if (ret == 0) { + kunit_info(suite, "found misc device minor %d available\n", + miscstat.minor); + miscdev_test_ranges[2].minor = miscstat.minor; + misc_deregister(&miscstat); + } else { + return ret; + } + + for (i = MISC_DYNAMIC_MINOR - 1; i > miscdev_test_ranges[1].minor; i--) { + miscstat.minor = i; + ret = misc_register(&miscstat); + if (ret == 0) + break; + } + + if (ret == 0) { + kunit_info(suite, "found misc device minor %d available\n", + miscstat.minor); + miscdev_test_ranges[3].minor = miscstat.minor; + misc_deregister(&miscstat); + } + + return ret; +} + +static bool is_valid_dynamic_minor(int minor) +{ + if (minor < 0) + return false; + return minor > MISC_DYNAMIC_MINOR; +} + +static int miscdev_test_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations miscdev_test_fops = { + .open = miscdev_test_open, +}; + +static void __init miscdev_test_can_open(struct kunit *test, struct miscdevice *misc) +{ + int ret; + struct file *filp; + char *devname; + + devname = kasprintf(GFP_KERNEL, "/dev/%s", misc->name); + ret = init_mknod(devname, S_IFCHR | 0600, + new_encode_dev(MKDEV(MISC_MAJOR, misc->minor))); + if (ret != 0) + KUNIT_FAIL(test, "failed to create node\n"); + + filp = filp_open(devname, O_RDONLY, 0); + if (IS_ERR(filp)) + KUNIT_FAIL(test, "failed to open misc device: %ld\n", PTR_ERR(filp)); + else + fput(filp); + + init_unlink(devname); + kfree(devname); +} + +static void __init miscdev_test_static_basic(struct kunit *test) +{ + struct miscdevice misc_test = { + .name = "misc_test", + .fops = &miscdev_test_fops, + }; + int ret; + const struct miscdev_test_case *params = test->param_value; + + misc_test.minor = params->minor; + + ret = misc_register(&misc_test); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor); + + if (ret == 0) { + miscdev_test_can_open(test, &misc_test); + misc_deregister(&misc_test); + } +} + +static void __init miscdev_test_dynamic_basic(struct kunit *test) +{ + struct miscdevice misc_test = { + .minor = MISC_DYNAMIC_MINOR, + .name = "misc_test", + .fops = &miscdev_test_fops, + }; + int ret; + + ret = misc_register(&misc_test); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc_test.minor)); + + if (ret == 0) { + miscdev_test_can_open(test, &misc_test); + misc_deregister(&misc_test); + } +} + +static void miscdev_test_twice(struct kunit *test) +{ + struct miscdevice misc_test = { + .name = "misc_test", + .fops = &miscdev_test_fops, + }; + int ret; + const struct miscdev_test_case *params = test->param_value; + + misc_test.minor = params->minor; + + ret = misc_register(&misc_test); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor); + if (ret == 0) + misc_deregister(&misc_test); + + ret = misc_register(&misc_test); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor); + if (ret == 0) + misc_deregister(&misc_test); +} + +static void miscdev_test_duplicate_minor(struct kunit *test) +{ + struct miscdevice misc1 = { + .name = "misc1", + .fops = &miscdev_test_fops, + }; + struct miscdevice misc2 = { + .name = "misc2", + .fops = &miscdev_test_fops, + }; + int ret; + const struct miscdev_test_case *params = test->param_value; + + misc1.minor = params->minor; + misc2.minor = params->minor; + + ret = misc_register(&misc1); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, misc1.minor, params->minor); + + ret = misc_register(&misc2); + KUNIT_EXPECT_EQ(test, ret, -EBUSY); + if (ret == 0) + misc_deregister(&misc2); + + misc_deregister(&misc1); +} + +static void miscdev_test_duplicate_name(struct kunit *test) +{ + struct miscdevice misc1 = { + .minor = MISC_DYNAMIC_MINOR, + .name = "misc1", + .fops = &miscdev_test_fops, + }; + struct miscdevice misc2 = { + .minor = MISC_DYNAMIC_MINOR, + .name = "misc1", + .fops = &miscdev_test_fops, + }; + int ret; + + ret = misc_register(&misc1); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc1.minor)); + + ret = misc_register(&misc2); + KUNIT_EXPECT_EQ(test, ret, -EEXIST); + if (ret == 0) + misc_deregister(&misc2); + + misc_deregister(&misc1); +} + +/* + * Test that after a duplicate name failure, the reserved minor number is + * freed to be allocated next. + */ +static void miscdev_test_duplicate_name_leak(struct kunit *test) +{ + struct miscdevice misc1 = { + .minor = MISC_DYNAMIC_MINOR, + .name = "misc1", + .fops = &miscdev_test_fops, + }; + struct miscdevice misc2 = { + .minor = MISC_DYNAMIC_MINOR, + .name = "misc1", + .fops = &miscdev_test_fops, + }; + struct miscdevice misc3 = { + .minor = MISC_DYNAMIC_MINOR, + .name = "misc3", + .fops = &miscdev_test_fops, + }; + int ret; + int dyn_minor; + + ret = misc_register(&misc1); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc1.minor)); + + /* + * Find out what is the next minor number available. + */ + ret = misc_register(&misc3); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc3.minor)); + dyn_minor = misc3.minor; + misc_deregister(&misc3); + misc3.minor = MISC_DYNAMIC_MINOR; + + ret = misc_register(&misc2); + KUNIT_EXPECT_EQ(test, ret, -EEXIST); + if (ret == 0) + misc_deregister(&misc2); + + /* + * Now check that we can still get the same minor we found before. + */ + ret = misc_register(&misc3); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc3.minor)); + KUNIT_EXPECT_EQ(test, misc3.minor, dyn_minor); + misc_deregister(&misc3); + + misc_deregister(&misc1); +} + +/* + * Try to register a static minor with a duplicate name. That might not + * deallocate the minor, preventing it from being used again. + */ +static void miscdev_test_duplicate_error(struct kunit *test) +{ + struct miscdevice miscdyn = { + .minor = MISC_DYNAMIC_MINOR, + .name = "name1", + .fops = &miscdev_test_fops, + }; + struct miscdevice miscstat = { + .name = "name1", + .fops = &miscdev_test_fops, + }; + struct miscdevice miscnew = { + .name = "name2", + .fops = &miscdev_test_fops, + }; + int ret; + const struct miscdev_test_case *params = test->param_value; + + miscstat.minor = params->minor; + miscnew.minor = params->minor; + + ret = misc_register(&miscdyn); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor)); + + ret = misc_register(&miscstat); + KUNIT_EXPECT_EQ(test, ret, -EEXIST); + if (ret == 0) + misc_deregister(&miscstat); + + ret = misc_register(&miscnew); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, miscnew.minor, params->minor); + if (ret == 0) + misc_deregister(&miscnew); + + misc_deregister(&miscdyn); +} + +static void __init miscdev_test_dynamic_only_range(struct kunit *test) +{ + int ret; + struct miscdevice *miscdev; + const int dynamic_minors = 256; + int i; + + miscdev = kunit_kmalloc_array(test, dynamic_minors, + sizeof(struct miscdevice), + GFP_KERNEL | __GFP_ZERO); + + for (i = 0; i < dynamic_minors; i++) { + miscdev[i].minor = MISC_DYNAMIC_MINOR; + miscdev[i].name = kasprintf(GFP_KERNEL, "misc_test%d", i); + miscdev[i].fops = &miscdev_test_fops; + ret = misc_register(&miscdev[i]); + if (ret != 0) + break; + /* + * This is the bug we are looking for! + * We asked for a dynamic minor and got a minor in the static range space. + */ + if (miscdev[i].minor >= 0 && miscdev[i].minor <= 15) { + KUNIT_FAIL(test, "misc_register allocated minor %d\n", miscdev[i].minor); + i++; + break; + } + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor)); + } + + for (i--; i >= 0; i--) { + miscdev_test_can_open(test, &miscdev[i]); + misc_deregister(&miscdev[i]); + kfree_const(miscdev[i].name); + } + + KUNIT_EXPECT_EQ(test, ret, 0); +} + +static void __init miscdev_test_collision(struct kunit *test) +{ + int ret; + struct miscdevice *miscdev; + struct miscdevice miscstat = { + .name = "miscstat", + .fops = &miscdev_test_fops, + }; + const int dynamic_minors = 256; + int i; + + miscdev = kunit_kmalloc_array(test, dynamic_minors, + sizeof(struct miscdevice), + GFP_KERNEL | __GFP_ZERO); + + miscstat.minor = miscdev_test_ranges[0].minor; + ret = misc_register(&miscstat); + KUNIT_ASSERT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, miscstat.minor, miscdev_test_ranges[0].minor); + + for (i = 0; i < dynamic_minors; i++) { + miscdev[i].minor = MISC_DYNAMIC_MINOR; + miscdev[i].name = kasprintf(GFP_KERNEL, "misc_test%d", i); + miscdev[i].fops = &miscdev_test_fops; + ret = misc_register(&miscdev[i]); + if (ret != 0) + break; + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor)); + } + + for (i--; i >= 0; i--) { + miscdev_test_can_open(test, &miscdev[i]); + misc_deregister(&miscdev[i]); + kfree_const(miscdev[i].name); + } + + misc_deregister(&miscstat); + + KUNIT_EXPECT_EQ(test, ret, 0); +} + +static void __init miscdev_test_collision_reverse(struct kunit *test) +{ + int ret; + struct miscdevice *miscdev; + struct miscdevice miscstat = { + .name = "miscstat", + .fops = &miscdev_test_fops, + }; + const int dynamic_minors = 256; + int i; + + miscdev = kunit_kmalloc_array(test, dynamic_minors, + sizeof(struct miscdevice), + GFP_KERNEL | __GFP_ZERO); + + for (i = 0; i < dynamic_minors; i++) { + miscdev[i].minor = MISC_DYNAMIC_MINOR; + miscdev[i].name = kasprintf(GFP_KERNEL, "misc_test%d", i); + miscdev[i].fops = &miscdev_test_fops; + ret = misc_register(&miscdev[i]); + if (ret != 0) + break; + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor)); + } + + KUNIT_EXPECT_EQ(test, ret, 0); + + miscstat.minor = miscdev_test_ranges[0].minor; + ret = misc_register(&miscstat); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, miscstat.minor, miscdev_test_ranges[0].minor); + if (ret == 0) + misc_deregister(&miscstat); + + for (i--; i >= 0; i--) { + miscdev_test_can_open(test, &miscdev[i]); + misc_deregister(&miscdev[i]); + kfree_const(miscdev[i].name); + } +} + +static void __init miscdev_test_conflict(struct kunit *test) +{ + int ret; + struct miscdevice miscdyn = { + .name = "miscdyn", + .minor = MISC_DYNAMIC_MINOR, + .fops = &miscdev_test_fops, + }; + struct miscdevice miscstat = { + .name = "miscstat", + .fops = &miscdev_test_fops, + }; + + ret = misc_register(&miscdyn); + KUNIT_ASSERT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor)); + + /* + * Try to register a static minor with the same minor as the + * dynamic one. + */ + miscstat.minor = miscdyn.minor; + ret = misc_register(&miscstat); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + if (ret == 0) + misc_deregister(&miscstat); + + miscdev_test_can_open(test, &miscdyn); + + misc_deregister(&miscdyn); +} + +static void __init miscdev_test_conflict_reverse(struct kunit *test) +{ + int ret; + struct miscdevice miscdyn = { + .name = "miscdyn", + .minor = MISC_DYNAMIC_MINOR, + .fops = &miscdev_test_fops, + }; + struct miscdevice miscstat = { + .name = "miscstat", + .fops = &miscdev_test_fops, + }; + + /* + * Find the first available dynamic minor to use it as a static + * minor later on. + */ + ret = misc_register(&miscdyn); + KUNIT_ASSERT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor)); + miscstat.minor = miscdyn.minor; + misc_deregister(&miscdyn); + + ret = misc_register(&miscstat); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + if (ret == 0) + misc_deregister(&miscstat); + + /* + * Try to register a dynamic minor after registering a static minor + * within the dynamic range. It should work but get a different + * minor. + */ + miscdyn.minor = MISC_DYNAMIC_MINOR; + ret = misc_register(&miscdyn); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, miscdyn.minor, miscstat.minor); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor)); + if (ret == 0) + misc_deregister(&miscdyn); +} + +/* Take minor(> MISC_DYNAMIC_MINOR) as invalid when register miscdevice */ +static void miscdev_test_invalid_input(struct kunit *test) +{ + struct miscdevice misc_test = { + .minor = MISC_DYNAMIC_MINOR + 1, + .name = "misc_test", + .fops = &miscdev_test_fops, + }; + int ret; + + ret = misc_register(&misc_test); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + if (ret == 0) + misc_deregister(&misc_test); +} + +/* + * Verify if @miscdyn_a can still be registered successfully without + * reinitialization even if its minor ever owned was requested by + * another miscdevice such as @miscdyn_b. + */ +static void miscdev_test_dynamic_reentry(struct kunit *test) +{ + struct miscdevice miscdyn_a = { + .name = "miscdyn_a", + .minor = MISC_DYNAMIC_MINOR, + .fops = &miscdev_test_fops, + }; + struct miscdevice miscdyn_b = { + .name = "miscdyn_b", + .minor = MISC_DYNAMIC_MINOR, + .fops = &miscdev_test_fops, + }; + int ret, minor_a; + + ret = misc_register(&miscdyn_a); + KUNIT_ASSERT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn_a.minor)); + minor_a = miscdyn_a.minor; + if (ret != 0) + return; + misc_deregister(&miscdyn_a); + + ret = misc_register(&miscdyn_b); + KUNIT_ASSERT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, miscdyn_b.minor, minor_a); + if (ret != 0) + return; + + ret = misc_register(&miscdyn_a); + KUNIT_ASSERT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn_a.minor)); + KUNIT_EXPECT_NE(test, miscdyn_a.minor, miscdyn_b.minor); + if (ret == 0) + misc_deregister(&miscdyn_a); + + misc_deregister(&miscdyn_b); +} + +static struct kunit_case test_cases[] = { + KUNIT_CASE(kunit_static_minor), + KUNIT_CASE(kunit_misc_dynamic_minor), + KUNIT_CASE(miscdev_test_invalid_input), + KUNIT_CASE_PARAM(miscdev_test_twice, miscdev_gen_params), + KUNIT_CASE_PARAM(miscdev_test_duplicate_minor, miscdev_gen_params), + KUNIT_CASE(miscdev_test_duplicate_name), + KUNIT_CASE(miscdev_test_duplicate_name_leak), + KUNIT_CASE_PARAM(miscdev_test_duplicate_error, miscdev_gen_params), + KUNIT_CASE(miscdev_test_dynamic_reentry), + {} +}; + +static struct kunit_suite test_suite = { + .name = "miscdev", + .suite_init = miscdev_find_minors, + .test_cases = test_cases, +}; +kunit_test_suite(test_suite); + +static struct kunit_case __refdata test_init_cases[] = { + KUNIT_CASE_PARAM(miscdev_test_static_basic, miscdev_gen_params), + KUNIT_CASE(miscdev_test_dynamic_basic), + KUNIT_CASE(miscdev_test_dynamic_only_range), + KUNIT_CASE(miscdev_test_collision), + KUNIT_CASE(miscdev_test_collision_reverse), + KUNIT_CASE(miscdev_test_conflict), + KUNIT_CASE(miscdev_test_conflict_reverse), + {} +}; + +static struct kunit_suite test_init_suite = { + .name = "miscdev_init", + .suite_init = miscdev_find_minors, + .test_cases = test_init_cases, +}; +kunit_test_init_section_suite(test_init_suite); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Vimal Agrawal"); +MODULE_AUTHOR("Thadeu Lima de Souza Cascardo <cascardo@igalia.com>"); +MODULE_DESCRIPTION("Test module for misc character devices"); diff --git a/drivers/char/mwave/3780i.c b/drivers/char/mwave/3780i.c deleted file mode 100644 index 4a8937f80570..000000000000 --- a/drivers/char/mwave/3780i.c +++ /dev/null @@ -1,738 +0,0 @@ -/* -* -* 3780i.c -- helper routines for the 3780i DSP -* -* -* Written By: Mike Sullivan IBM Corporation -* -* Copyright (C) 1999 IBM Corporation -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* NO WARRANTY -* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR -* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT -* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, -* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is -* solely responsible for determining the appropriateness of using and -* distributing the Program and assumes all risks associated with its -* exercise of rights under this Agreement, including but not limited to -* the risks and costs of program errors, damage to or loss of data, -* programs or equipment, and unavailability or interruption of operations. -* -* DISCLAIMER OF LIABILITY -* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* -* 10/23/2000 - Alpha Release -* First release to the public -*/ - -#include <linux/kernel.h> -#include <linux/unistd.h> -#include <linux/delay.h> -#include <linux/ioport.h> -#include <linux/bitops.h> -#include <linux/sched.h> /* cond_resched() */ - -#include <asm/io.h> -#include <linux/uaccess.h> -#include <asm/irq.h> -#include "smapi.h" -#include "mwavedd.h" -#include "3780i.h" - -static DEFINE_SPINLOCK(dsp_lock); - -static void PaceMsaAccess(unsigned short usDspBaseIO) -{ - cond_resched(); - udelay(100); - cond_resched(); -} - -unsigned short dsp3780I_ReadMsaCfg(unsigned short usDspBaseIO, - unsigned long ulMsaAddr) -{ - unsigned long flags; - unsigned short val; - - PRINTK_3(TRACE_3780I, - "3780i::dsp3780I_ReadMsaCfg entry usDspBaseIO %x ulMsaAddr %lx\n", - usDspBaseIO, ulMsaAddr); - - spin_lock_irqsave(&dsp_lock, flags); - OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulMsaAddr); - OutWordDsp(DSP_MsaAddrHigh, (unsigned short) (ulMsaAddr >> 16)); - val = InWordDsp(DSP_MsaDataDSISHigh); - spin_unlock_irqrestore(&dsp_lock, flags); - - PRINTK_2(TRACE_3780I, "3780i::dsp3780I_ReadMsaCfg exit val %x\n", val); - - return val; -} - -void dsp3780I_WriteMsaCfg(unsigned short usDspBaseIO, - unsigned long ulMsaAddr, unsigned short usValue) -{ - unsigned long flags; - - PRINTK_4(TRACE_3780I, - "3780i::dsp3780i_WriteMsaCfg entry usDspBaseIO %x ulMsaAddr %lx usValue %x\n", - usDspBaseIO, ulMsaAddr, usValue); - - spin_lock_irqsave(&dsp_lock, flags); - OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulMsaAddr); - OutWordDsp(DSP_MsaAddrHigh, (unsigned short) (ulMsaAddr >> 16)); - OutWordDsp(DSP_MsaDataDSISHigh, usValue); - spin_unlock_irqrestore(&dsp_lock, flags); -} - -static void dsp3780I_WriteGenCfg(unsigned short usDspBaseIO, unsigned uIndex, - unsigned char ucValue) -{ - DSP_ISA_SLAVE_CONTROL rSlaveControl; - DSP_ISA_SLAVE_CONTROL rSlaveControl_Save; - - - PRINTK_4(TRACE_3780I, - "3780i::dsp3780i_WriteGenCfg entry usDspBaseIO %x uIndex %x ucValue %x\n", - usDspBaseIO, uIndex, ucValue); - - MKBYTE(rSlaveControl) = InByteDsp(DSP_IsaSlaveControl); - - PRINTK_2(TRACE_3780I, - "3780i::dsp3780i_WriteGenCfg rSlaveControl %x\n", - MKBYTE(rSlaveControl)); - - rSlaveControl_Save = rSlaveControl; - rSlaveControl.ConfigMode = true; - - PRINTK_2(TRACE_3780I, - "3780i::dsp3780i_WriteGenCfg entry rSlaveControl+ConfigMode %x\n", - MKBYTE(rSlaveControl)); - - OutByteDsp(DSP_IsaSlaveControl, MKBYTE(rSlaveControl)); - OutByteDsp(DSP_ConfigAddress, (unsigned char) uIndex); - OutByteDsp(DSP_ConfigData, ucValue); - OutByteDsp(DSP_IsaSlaveControl, MKBYTE(rSlaveControl_Save)); - - PRINTK_1(TRACE_3780I, "3780i::dsp3780i_WriteGenCfg exit\n"); - - -} - -#if 0 -unsigned char dsp3780I_ReadGenCfg(unsigned short usDspBaseIO, - unsigned uIndex) -{ - DSP_ISA_SLAVE_CONTROL rSlaveControl; - DSP_ISA_SLAVE_CONTROL rSlaveControl_Save; - unsigned char ucValue; - - - PRINTK_3(TRACE_3780I, - "3780i::dsp3780i_ReadGenCfg entry usDspBaseIO %x uIndex %x\n", - usDspBaseIO, uIndex); - - MKBYTE(rSlaveControl) = InByteDsp(DSP_IsaSlaveControl); - rSlaveControl_Save = rSlaveControl; - rSlaveControl.ConfigMode = true; - OutByteDsp(DSP_IsaSlaveControl, MKBYTE(rSlaveControl)); - OutByteDsp(DSP_ConfigAddress, (unsigned char) uIndex); - ucValue = InByteDsp(DSP_ConfigData); - OutByteDsp(DSP_IsaSlaveControl, MKBYTE(rSlaveControl_Save)); - - PRINTK_2(TRACE_3780I, - "3780i::dsp3780i_ReadGenCfg exit ucValue %x\n", ucValue); - - - return ucValue; -} -#endif /* 0 */ - -int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings, - unsigned short *pIrqMap, - unsigned short *pDmaMap) -{ - unsigned long flags; - unsigned short usDspBaseIO = pSettings->usDspBaseIO; - int i; - DSP_UART_CFG_1 rUartCfg1; - DSP_UART_CFG_2 rUartCfg2; - DSP_HBRIDGE_CFG_1 rHBridgeCfg1; - DSP_HBRIDGE_CFG_2 rHBridgeCfg2; - DSP_BUSMASTER_CFG_1 rBusmasterCfg1; - DSP_BUSMASTER_CFG_2 rBusmasterCfg2; - DSP_ISA_PROT_CFG rIsaProtCfg; - DSP_POWER_MGMT_CFG rPowerMgmtCfg; - DSP_HBUS_TIMER_CFG rHBusTimerCfg; - DSP_LBUS_TIMEOUT_DISABLE rLBusTimeoutDisable; - DSP_CHIP_RESET rChipReset; - DSP_CLOCK_CONTROL_1 rClockControl1; - DSP_CLOCK_CONTROL_2 rClockControl2; - DSP_ISA_SLAVE_CONTROL rSlaveControl; - DSP_HBRIDGE_CONTROL rHBridgeControl; - unsigned short ChipID = 0; - unsigned short tval; - - - PRINTK_2(TRACE_3780I, - "3780i::dsp3780I_EnableDSP entry pSettings->bDSPEnabled %x\n", - pSettings->bDSPEnabled); - - - if (!pSettings->bDSPEnabled) { - PRINTK_ERROR( KERN_ERR "3780i::dsp3780I_EnableDSP: Error: DSP not enabled. Aborting.\n" ); - return -EIO; - } - - - PRINTK_2(TRACE_3780I, - "3780i::dsp3780i_EnableDSP entry pSettings->bModemEnabled %x\n", - pSettings->bModemEnabled); - - if (pSettings->bModemEnabled) { - rUartCfg1.Reserved = rUartCfg2.Reserved = 0; - rUartCfg1.IrqActiveLow = pSettings->bUartIrqActiveLow; - rUartCfg1.IrqPulse = pSettings->bUartIrqPulse; - rUartCfg1.Irq = - (unsigned char) pIrqMap[pSettings->usUartIrq]; - switch (pSettings->usUartBaseIO) { - case 0x03F8: - rUartCfg1.BaseIO = 0; - break; - case 0x02F8: - rUartCfg1.BaseIO = 1; - break; - case 0x03E8: - rUartCfg1.BaseIO = 2; - break; - case 0x02E8: - rUartCfg1.BaseIO = 3; - break; - } - rUartCfg2.Enable = true; - } - - rHBridgeCfg1.Reserved = rHBridgeCfg2.Reserved = 0; - rHBridgeCfg1.IrqActiveLow = pSettings->bDspIrqActiveLow; - rHBridgeCfg1.IrqPulse = pSettings->bDspIrqPulse; - rHBridgeCfg1.Irq = (unsigned char) pIrqMap[pSettings->usDspIrq]; - rHBridgeCfg1.AccessMode = 1; - rHBridgeCfg2.Enable = true; - - - rBusmasterCfg2.Reserved = 0; - rBusmasterCfg1.Dma = (unsigned char) pDmaMap[pSettings->usDspDma]; - rBusmasterCfg1.NumTransfers = - (unsigned char) pSettings->usNumTransfers; - rBusmasterCfg1.ReRequest = (unsigned char) pSettings->usReRequest; - rBusmasterCfg1.MEMCS16 = pSettings->bEnableMEMCS16; - rBusmasterCfg2.IsaMemCmdWidth = - (unsigned char) pSettings->usIsaMemCmdWidth; - - - rIsaProtCfg.Reserved = 0; - rIsaProtCfg.GateIOCHRDY = pSettings->bGateIOCHRDY; - - rPowerMgmtCfg.Reserved = 0; - rPowerMgmtCfg.Enable = pSettings->bEnablePwrMgmt; - - rHBusTimerCfg.LoadValue = - (unsigned char) pSettings->usHBusTimerLoadValue; - - rLBusTimeoutDisable.Reserved = 0; - rLBusTimeoutDisable.DisableTimeout = - pSettings->bDisableLBusTimeout; - - MKWORD(rChipReset) = ~pSettings->usChipletEnable; - - rClockControl1.Reserved1 = rClockControl1.Reserved2 = 0; - rClockControl1.N_Divisor = pSettings->usN_Divisor; - rClockControl1.M_Multiplier = pSettings->usM_Multiplier; - - rClockControl2.Reserved = 0; - rClockControl2.PllBypass = pSettings->bPllBypass; - - /* Issue a soft reset to the chip */ - /* Note: Since we may be coming in with 3780i clocks suspended, we must keep - * soft-reset active for 10ms. - */ - rSlaveControl.ClockControl = 0; - rSlaveControl.SoftReset = true; - rSlaveControl.ConfigMode = false; - rSlaveControl.Reserved = 0; - - PRINTK_4(TRACE_3780I, - "3780i::dsp3780i_EnableDSP usDspBaseIO %x index %x taddr %x\n", - usDspBaseIO, DSP_IsaSlaveControl, - usDspBaseIO + DSP_IsaSlaveControl); - - PRINTK_2(TRACE_3780I, - "3780i::dsp3780i_EnableDSP rSlaveContrl %x\n", - MKWORD(rSlaveControl)); - - spin_lock_irqsave(&dsp_lock, flags); - OutWordDsp(DSP_IsaSlaveControl, MKWORD(rSlaveControl)); - MKWORD(tval) = InWordDsp(DSP_IsaSlaveControl); - - PRINTK_2(TRACE_3780I, - "3780i::dsp3780i_EnableDSP rSlaveControl 2 %x\n", tval); - - - for (i = 0; i < 11; i++) - udelay(2000); - - rSlaveControl.SoftReset = false; - OutWordDsp(DSP_IsaSlaveControl, MKWORD(rSlaveControl)); - - MKWORD(tval) = InWordDsp(DSP_IsaSlaveControl); - - PRINTK_2(TRACE_3780I, - "3780i::dsp3780i_EnableDSP rSlaveControl 3 %x\n", tval); - - - /* Program our general configuration registers */ - WriteGenCfg(DSP_HBridgeCfg1Index, MKBYTE(rHBridgeCfg1)); - WriteGenCfg(DSP_HBridgeCfg2Index, MKBYTE(rHBridgeCfg2)); - WriteGenCfg(DSP_BusMasterCfg1Index, MKBYTE(rBusmasterCfg1)); - WriteGenCfg(DSP_BusMasterCfg2Index, MKBYTE(rBusmasterCfg2)); - WriteGenCfg(DSP_IsaProtCfgIndex, MKBYTE(rIsaProtCfg)); - WriteGenCfg(DSP_PowerMgCfgIndex, MKBYTE(rPowerMgmtCfg)); - WriteGenCfg(DSP_HBusTimerCfgIndex, MKBYTE(rHBusTimerCfg)); - - if (pSettings->bModemEnabled) { - WriteGenCfg(DSP_UartCfg1Index, MKBYTE(rUartCfg1)); - WriteGenCfg(DSP_UartCfg2Index, MKBYTE(rUartCfg2)); - } - - - rHBridgeControl.EnableDspInt = false; - rHBridgeControl.MemAutoInc = true; - rHBridgeControl.IoAutoInc = false; - rHBridgeControl.DiagnosticMode = false; - - PRINTK_3(TRACE_3780I, - "3780i::dsp3780i_EnableDSP DSP_HBridgeControl %x rHBridgeControl %x\n", - DSP_HBridgeControl, MKWORD(rHBridgeControl)); - - OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl)); - spin_unlock_irqrestore(&dsp_lock, flags); - WriteMsaCfg(DSP_LBusTimeoutDisable, MKWORD(rLBusTimeoutDisable)); - WriteMsaCfg(DSP_ClockControl_1, MKWORD(rClockControl1)); - WriteMsaCfg(DSP_ClockControl_2, MKWORD(rClockControl2)); - WriteMsaCfg(DSP_ChipReset, MKWORD(rChipReset)); - - ChipID = ReadMsaCfg(DSP_ChipID); - - PRINTK_2(TRACE_3780I, - "3780i::dsp3780I_EnableDSP exiting bRC=true, ChipID %x\n", - ChipID); - - return 0; -} - -int dsp3780I_DisableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings) -{ - unsigned long flags; - unsigned short usDspBaseIO = pSettings->usDspBaseIO; - DSP_ISA_SLAVE_CONTROL rSlaveControl; - - - PRINTK_1(TRACE_3780I, "3780i::dsp3780i_DisableDSP entry\n"); - - rSlaveControl.ClockControl = 0; - rSlaveControl.SoftReset = true; - rSlaveControl.ConfigMode = false; - rSlaveControl.Reserved = 0; - spin_lock_irqsave(&dsp_lock, flags); - OutWordDsp(DSP_IsaSlaveControl, MKWORD(rSlaveControl)); - - udelay(5); - - rSlaveControl.ClockControl = 1; - OutWordDsp(DSP_IsaSlaveControl, MKWORD(rSlaveControl)); - spin_unlock_irqrestore(&dsp_lock, flags); - - udelay(5); - - - PRINTK_1(TRACE_3780I, "3780i::dsp3780i_DisableDSP exit\n"); - - return 0; -} - -int dsp3780I_Reset(DSP_3780I_CONFIG_SETTINGS * pSettings) -{ - unsigned long flags; - unsigned short usDspBaseIO = pSettings->usDspBaseIO; - DSP_BOOT_DOMAIN rBootDomain; - DSP_HBRIDGE_CONTROL rHBridgeControl; - - - PRINTK_1(TRACE_3780I, "3780i::dsp3780i_Reset entry\n"); - - spin_lock_irqsave(&dsp_lock, flags); - /* Mask DSP to PC interrupt */ - MKWORD(rHBridgeControl) = InWordDsp(DSP_HBridgeControl); - - PRINTK_2(TRACE_3780I, "3780i::dsp3780i_Reset rHBridgeControl %x\n", - MKWORD(rHBridgeControl)); - - rHBridgeControl.EnableDspInt = false; - OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl)); - spin_unlock_irqrestore(&dsp_lock, flags); - - /* Reset the core via the boot domain register */ - rBootDomain.ResetCore = true; - rBootDomain.Halt = true; - rBootDomain.NMI = true; - rBootDomain.Reserved = 0; - - PRINTK_2(TRACE_3780I, "3780i::dsp3780i_Reset rBootDomain %x\n", - MKWORD(rBootDomain)); - - WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain)); - - /* Reset all the chiplets and then reactivate them */ - WriteMsaCfg(DSP_ChipReset, 0xFFFF); - udelay(5); - WriteMsaCfg(DSP_ChipReset, - (unsigned short) (~pSettings->usChipletEnable)); - - - PRINTK_1(TRACE_3780I, "3780i::dsp3780i_Reset exit bRC=0\n"); - - return 0; -} - - -int dsp3780I_Run(DSP_3780I_CONFIG_SETTINGS * pSettings) -{ - unsigned long flags; - unsigned short usDspBaseIO = pSettings->usDspBaseIO; - DSP_BOOT_DOMAIN rBootDomain; - DSP_HBRIDGE_CONTROL rHBridgeControl; - - - PRINTK_1(TRACE_3780I, "3780i::dsp3780i_Run entry\n"); - - - /* Transition the core to a running state */ - rBootDomain.ResetCore = true; - rBootDomain.Halt = false; - rBootDomain.NMI = true; - rBootDomain.Reserved = 0; - WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain)); - - udelay(5); - - rBootDomain.ResetCore = false; - WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain)); - udelay(5); - - rBootDomain.NMI = false; - WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain)); - udelay(5); - - /* Enable DSP to PC interrupt */ - spin_lock_irqsave(&dsp_lock, flags); - MKWORD(rHBridgeControl) = InWordDsp(DSP_HBridgeControl); - rHBridgeControl.EnableDspInt = true; - - PRINTK_2(TRACE_3780I, "3780i::dsp3780i_Run rHBridgeControl %x\n", - MKWORD(rHBridgeControl)); - - OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl)); - spin_unlock_irqrestore(&dsp_lock, flags); - - - PRINTK_1(TRACE_3780I, "3780i::dsp3780i_Run exit bRC=true\n"); - - return 0; -} - - -int dsp3780I_ReadDStore(unsigned short usDspBaseIO, void __user *pvBuffer, - unsigned uCount, unsigned long ulDSPAddr) -{ - unsigned long flags; - unsigned short __user *pusBuffer = pvBuffer; - unsigned short val; - - - PRINTK_5(TRACE_3780I, - "3780i::dsp3780I_ReadDStore entry usDspBaseIO %x, pusBuffer %p, uCount %x, ulDSPAddr %lx\n", - usDspBaseIO, pusBuffer, uCount, ulDSPAddr); - - - /* Set the initial MSA address. No adjustments need to be made to data store addresses */ - spin_lock_irqsave(&dsp_lock, flags); - OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulDSPAddr); - OutWordDsp(DSP_MsaAddrHigh, (unsigned short) (ulDSPAddr >> 16)); - spin_unlock_irqrestore(&dsp_lock, flags); - - /* Transfer the memory block */ - while (uCount-- != 0) { - spin_lock_irqsave(&dsp_lock, flags); - val = InWordDsp(DSP_MsaDataDSISHigh); - spin_unlock_irqrestore(&dsp_lock, flags); - if(put_user(val, pusBuffer++)) - return -EFAULT; - - PRINTK_3(TRACE_3780I, - "3780I::dsp3780I_ReadDStore uCount %x val %x\n", - uCount, val); - - PaceMsaAccess(usDspBaseIO); - } - - - PRINTK_1(TRACE_3780I, - "3780I::dsp3780I_ReadDStore exit bRC=true\n"); - - return 0; -} - -int dsp3780I_ReadAndClearDStore(unsigned short usDspBaseIO, - void __user *pvBuffer, unsigned uCount, - unsigned long ulDSPAddr) -{ - unsigned long flags; - unsigned short __user *pusBuffer = pvBuffer; - unsigned short val; - - - PRINTK_5(TRACE_3780I, - "3780i::dsp3780I_ReadAndDStore entry usDspBaseIO %x, pusBuffer %p, uCount %x, ulDSPAddr %lx\n", - usDspBaseIO, pusBuffer, uCount, ulDSPAddr); - - - /* Set the initial MSA address. No adjustments need to be made to data store addresses */ - spin_lock_irqsave(&dsp_lock, flags); - OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulDSPAddr); - OutWordDsp(DSP_MsaAddrHigh, (unsigned short) (ulDSPAddr >> 16)); - spin_unlock_irqrestore(&dsp_lock, flags); - - /* Transfer the memory block */ - while (uCount-- != 0) { - spin_lock_irqsave(&dsp_lock, flags); - val = InWordDsp(DSP_ReadAndClear); - spin_unlock_irqrestore(&dsp_lock, flags); - if(put_user(val, pusBuffer++)) - return -EFAULT; - - PRINTK_3(TRACE_3780I, - "3780I::dsp3780I_ReadAndCleanDStore uCount %x val %x\n", - uCount, val); - - PaceMsaAccess(usDspBaseIO); - } - - - PRINTK_1(TRACE_3780I, - "3780I::dsp3780I_ReadAndClearDStore exit bRC=true\n"); - - return 0; -} - - -int dsp3780I_WriteDStore(unsigned short usDspBaseIO, void __user *pvBuffer, - unsigned uCount, unsigned long ulDSPAddr) -{ - unsigned long flags; - unsigned short __user *pusBuffer = pvBuffer; - - - PRINTK_5(TRACE_3780I, - "3780i::dsp3780D_WriteDStore entry usDspBaseIO %x, pusBuffer %p, uCount %x, ulDSPAddr %lx\n", - usDspBaseIO, pusBuffer, uCount, ulDSPAddr); - - - /* Set the initial MSA address. No adjustments need to be made to data store addresses */ - spin_lock_irqsave(&dsp_lock, flags); - OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulDSPAddr); - OutWordDsp(DSP_MsaAddrHigh, (unsigned short) (ulDSPAddr >> 16)); - spin_unlock_irqrestore(&dsp_lock, flags); - - /* Transfer the memory block */ - while (uCount-- != 0) { - unsigned short val; - if(get_user(val, pusBuffer++)) - return -EFAULT; - spin_lock_irqsave(&dsp_lock, flags); - OutWordDsp(DSP_MsaDataDSISHigh, val); - spin_unlock_irqrestore(&dsp_lock, flags); - - PRINTK_3(TRACE_3780I, - "3780I::dsp3780I_WriteDStore uCount %x val %x\n", - uCount, val); - - PaceMsaAccess(usDspBaseIO); - } - - - PRINTK_1(TRACE_3780I, - "3780I::dsp3780D_WriteDStore exit bRC=true\n"); - - return 0; -} - - -int dsp3780I_ReadIStore(unsigned short usDspBaseIO, void __user *pvBuffer, - unsigned uCount, unsigned long ulDSPAddr) -{ - unsigned long flags; - unsigned short __user *pusBuffer = pvBuffer; - - PRINTK_5(TRACE_3780I, - "3780i::dsp3780I_ReadIStore entry usDspBaseIO %x, pusBuffer %p, uCount %x, ulDSPAddr %lx\n", - usDspBaseIO, pusBuffer, uCount, ulDSPAddr); - - /* - * Set the initial MSA address. To convert from an instruction store - * address to an MSA address - * shift the address two bits to the left and set bit 22 - */ - ulDSPAddr = (ulDSPAddr << 2) | (1 << 22); - spin_lock_irqsave(&dsp_lock, flags); - OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulDSPAddr); - OutWordDsp(DSP_MsaAddrHigh, (unsigned short) (ulDSPAddr >> 16)); - spin_unlock_irqrestore(&dsp_lock, flags); - - /* Transfer the memory block */ - while (uCount-- != 0) { - unsigned short val_lo, val_hi; - spin_lock_irqsave(&dsp_lock, flags); - val_lo = InWordDsp(DSP_MsaDataISLow); - val_hi = InWordDsp(DSP_MsaDataDSISHigh); - spin_unlock_irqrestore(&dsp_lock, flags); - if(put_user(val_lo, pusBuffer++)) - return -EFAULT; - if(put_user(val_hi, pusBuffer++)) - return -EFAULT; - - PRINTK_4(TRACE_3780I, - "3780I::dsp3780I_ReadIStore uCount %x val_lo %x val_hi %x\n", - uCount, val_lo, val_hi); - - PaceMsaAccess(usDspBaseIO); - - } - - PRINTK_1(TRACE_3780I, - "3780I::dsp3780I_ReadIStore exit bRC=true\n"); - - return 0; -} - - -int dsp3780I_WriteIStore(unsigned short usDspBaseIO, void __user *pvBuffer, - unsigned uCount, unsigned long ulDSPAddr) -{ - unsigned long flags; - unsigned short __user *pusBuffer = pvBuffer; - - PRINTK_5(TRACE_3780I, - "3780i::dsp3780I_WriteIStore entry usDspBaseIO %x, pusBuffer %p, uCount %x, ulDSPAddr %lx\n", - usDspBaseIO, pusBuffer, uCount, ulDSPAddr); - - - /* - * Set the initial MSA address. To convert from an instruction store - * address to an MSA address - * shift the address two bits to the left and set bit 22 - */ - ulDSPAddr = (ulDSPAddr << 2) | (1 << 22); - spin_lock_irqsave(&dsp_lock, flags); - OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulDSPAddr); - OutWordDsp(DSP_MsaAddrHigh, (unsigned short) (ulDSPAddr >> 16)); - spin_unlock_irqrestore(&dsp_lock, flags); - - /* Transfer the memory block */ - while (uCount-- != 0) { - unsigned short val_lo, val_hi; - if(get_user(val_lo, pusBuffer++)) - return -EFAULT; - if(get_user(val_hi, pusBuffer++)) - return -EFAULT; - spin_lock_irqsave(&dsp_lock, flags); - OutWordDsp(DSP_MsaDataISLow, val_lo); - OutWordDsp(DSP_MsaDataDSISHigh, val_hi); - spin_unlock_irqrestore(&dsp_lock, flags); - - PRINTK_4(TRACE_3780I, - "3780I::dsp3780I_WriteIStore uCount %x val_lo %x val_hi %x\n", - uCount, val_lo, val_hi); - - PaceMsaAccess(usDspBaseIO); - - } - - PRINTK_1(TRACE_3780I, - "3780I::dsp3780I_WriteIStore exit bRC=true\n"); - - return 0; -} - - -int dsp3780I_GetIPCSource(unsigned short usDspBaseIO, - unsigned short *pusIPCSource) -{ - unsigned long flags; - DSP_HBRIDGE_CONTROL rHBridgeControl; - unsigned short temp; - - - PRINTK_3(TRACE_3780I, - "3780i::dsp3780I_GetIPCSource entry usDspBaseIO %x pusIPCSource %p\n", - usDspBaseIO, pusIPCSource); - - /* - * Disable DSP to PC interrupts, read the interrupt register, - * clear the pending IPC bits, and reenable DSP to PC interrupts - */ - spin_lock_irqsave(&dsp_lock, flags); - MKWORD(rHBridgeControl) = InWordDsp(DSP_HBridgeControl); - rHBridgeControl.EnableDspInt = false; - OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl)); - - *pusIPCSource = InWordDsp(DSP_Interrupt); - temp = (unsigned short) ~(*pusIPCSource); - - PRINTK_3(TRACE_3780I, - "3780i::dsp3780I_GetIPCSource, usIPCSource %x ~ %x\n", - *pusIPCSource, temp); - - OutWordDsp(DSP_Interrupt, (unsigned short) ~(*pusIPCSource)); - - rHBridgeControl.EnableDspInt = true; - OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl)); - spin_unlock_irqrestore(&dsp_lock, flags); - - - PRINTK_2(TRACE_3780I, - "3780i::dsp3780I_GetIPCSource exit usIPCSource %x\n", - *pusIPCSource); - - return 0; -} diff --git a/drivers/char/mwave/3780i.h b/drivers/char/mwave/3780i.h deleted file mode 100644 index 95164246afd1..000000000000 --- a/drivers/char/mwave/3780i.h +++ /dev/null @@ -1,358 +0,0 @@ -/* -* -* 3780i.h -- declarations for 3780i.c -* -* -* Written By: Mike Sullivan IBM Corporation -* -* Copyright (C) 1999 IBM Corporation -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* NO WARRANTY -* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR -* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT -* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, -* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is -* solely responsible for determining the appropriateness of using and -* distributing the Program and assumes all risks associated with its -* exercise of rights under this Agreement, including but not limited to -* the risks and costs of program errors, damage to or loss of data, -* programs or equipment, and unavailability or interruption of operations. -* -* DISCLAIMER OF LIABILITY -* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* -* 10/23/2000 - Alpha Release -* First release to the public -*/ - -#ifndef _LINUX_3780I_H -#define _LINUX_3780I_H - -#include <asm/io.h> - -/* DSP I/O port offsets and definitions */ -#define DSP_IsaSlaveControl 0x0000 /* ISA slave control register */ -#define DSP_IsaSlaveStatus 0x0001 /* ISA slave status register */ -#define DSP_ConfigAddress 0x0002 /* General config address register */ -#define DSP_ConfigData 0x0003 /* General config data register */ -#define DSP_HBridgeControl 0x0002 /* HBridge control register */ -#define DSP_MsaAddrLow 0x0004 /* MSP System Address, low word */ -#define DSP_MsaAddrHigh 0x0006 /* MSP System Address, high word */ -#define DSP_MsaDataDSISHigh 0x0008 /* MSA data register: d-store word or high byte of i-store */ -#define DSP_MsaDataISLow 0x000A /* MSA data register: low word of i-store */ -#define DSP_ReadAndClear 0x000C /* MSA read and clear data register */ -#define DSP_Interrupt 0x000E /* Interrupt register (IPC source) */ - -typedef struct { - unsigned char ClockControl:1; /* RW: Clock control: 0=normal, 1=stop 3780i clocks */ - unsigned char SoftReset:1; /* RW: Soft reset 0=normal, 1=soft reset active */ - unsigned char ConfigMode:1; /* RW: Configuration mode, 0=normal, 1=config mode */ - unsigned short Reserved:13; /* 0: Reserved */ -} DSP_ISA_SLAVE_CONTROL; - - -typedef struct { - unsigned short EnableDspInt:1; /* RW: Enable DSP to X86 ISA interrupt 0=mask it, 1=enable it */ - unsigned short MemAutoInc:1; /* RW: Memory address auto increment, 0=disable, 1=enable */ - unsigned short IoAutoInc:1; /* RW: I/O address auto increment, 0=disable, 1=enable */ - unsigned short DiagnosticMode:1; /* RW: Disgnostic mode 0=nromal, 1=diagnostic mode */ - unsigned short IsaPacingTimer:12; /* R: ISA access pacing timer: count of core cycles stolen */ -} DSP_HBRIDGE_CONTROL; - - -/* DSP register indexes used with the configuration register address (index) register */ -#define DSP_UartCfg1Index 0x0003 /* UART config register 1 */ -#define DSP_UartCfg2Index 0x0004 /* UART config register 2 */ -#define DSP_HBridgeCfg1Index 0x0007 /* HBridge config register 1 */ -#define DSP_HBridgeCfg2Index 0x0008 /* HBridge config register 2 */ -#define DSP_BusMasterCfg1Index 0x0009 /* ISA bus master config register 1 */ -#define DSP_BusMasterCfg2Index 0x000A /* ISA bus master config register 2 */ -#define DSP_IsaProtCfgIndex 0x000F /* ISA protocol control register */ -#define DSP_PowerMgCfgIndex 0x0010 /* Low poser suspend/resume enable */ -#define DSP_HBusTimerCfgIndex 0x0011 /* HBUS timer load value */ - -typedef struct { - unsigned char IrqActiveLow:1; /* RW: IRQ active high or low: 0=high, 1=low */ - unsigned char IrqPulse:1; /* RW: IRQ pulse or level: 0=level, 1=pulse */ - unsigned char Irq:3; /* RW: IRQ selection */ - unsigned char BaseIO:2; /* RW: Base I/O selection */ - unsigned char Reserved:1; /* 0: Reserved */ -} DSP_UART_CFG_1; - -typedef struct { - unsigned char Enable:1; /* RW: Enable I/O and IRQ: 0=false, 1=true */ - unsigned char Reserved:7; /* 0: Reserved */ -} DSP_UART_CFG_2; - -typedef struct { - unsigned char IrqActiveLow:1; /* RW: IRQ active high=0 or low=1 */ - unsigned char IrqPulse:1; /* RW: IRQ pulse=1 or level=0 */ - unsigned char Irq:3; /* RW: IRQ selection */ - unsigned char AccessMode:1; /* RW: 16-bit register access method 0=byte, 1=word */ - unsigned char Reserved:2; /* 0: Reserved */ -} DSP_HBRIDGE_CFG_1; - -typedef struct { - unsigned char Enable:1; /* RW: enable I/O and IRQ: 0=false, 1=true */ - unsigned char Reserved:7; /* 0: Reserved */ -} DSP_HBRIDGE_CFG_2; - - -typedef struct { - unsigned char Dma:3; /* RW: DMA channel selection */ - unsigned char NumTransfers:2; /* RW: Maximum # of transfers once being granted the ISA bus */ - unsigned char ReRequest:2; /* RW: Minimum delay between releasing the ISA bus and requesting it again */ - unsigned char MEMCS16:1; /* RW: ISA signal MEMCS16: 0=disabled, 1=enabled */ -} DSP_BUSMASTER_CFG_1; - -typedef struct { - unsigned char IsaMemCmdWidth:2; /* RW: ISA memory command width */ - unsigned char Reserved:6; /* 0: Reserved */ -} DSP_BUSMASTER_CFG_2; - - -typedef struct { - unsigned char GateIOCHRDY:1; /* RW: Enable IOCHRDY gating: 0=false, 1=true */ - unsigned char Reserved:7; /* 0: Reserved */ -} DSP_ISA_PROT_CFG; - -typedef struct { - unsigned char Enable:1; /* RW: Enable low power suspend/resume 0=false, 1=true */ - unsigned char Reserved:7; /* 0: Reserved */ -} DSP_POWER_MGMT_CFG; - -typedef struct { - unsigned char LoadValue:8; /* RW: HBUS timer load value */ -} DSP_HBUS_TIMER_CFG; - - - -/* DSP registers that exist in MSA I/O space */ -#define DSP_ChipID 0x80000000 -#define DSP_MspBootDomain 0x80000580 -#define DSP_LBusTimeoutDisable 0x80000580 -#define DSP_ClockControl_1 0x8000058A -#define DSP_ClockControl_2 0x8000058C -#define DSP_ChipReset 0x80000588 -#define DSP_GpioModeControl_15_8 0x80000082 -#define DSP_GpioDriverEnable_15_8 0x80000076 -#define DSP_GpioOutputData_15_8 0x80000072 - -typedef struct { - unsigned short NMI:1; /* RW: non maskable interrupt */ - unsigned short Halt:1; /* RW: Halt MSP clock */ - unsigned short ResetCore:1; /* RW: Reset MSP core interface */ - unsigned short Reserved:13; /* 0: Reserved */ -} DSP_BOOT_DOMAIN; - -typedef struct { - unsigned short DisableTimeout:1; /* RW: Disable LBus timeout */ - unsigned short Reserved:15; /* 0: Reserved */ -} DSP_LBUS_TIMEOUT_DISABLE; - -typedef struct { - unsigned short Memory:1; /* RW: Reset memory interface */ - unsigned short SerialPort1:1; /* RW: Reset serial port 1 interface */ - unsigned short SerialPort2:1; /* RW: Reset serial port 2 interface */ - unsigned short SerialPort3:1; /* RW: Reset serial port 3 interface */ - unsigned short Gpio:1; /* RW: Reset GPIO interface */ - unsigned short Dma:1; /* RW: Reset DMA interface */ - unsigned short SoundBlaster:1; /* RW: Reset soundblaster interface */ - unsigned short Uart:1; /* RW: Reset UART interface */ - unsigned short Midi:1; /* RW: Reset MIDI interface */ - unsigned short IsaMaster:1; /* RW: Reset ISA master interface */ - unsigned short Reserved:6; /* 0: Reserved */ -} DSP_CHIP_RESET; - -typedef struct { - unsigned short N_Divisor:6; /* RW: (N) PLL output clock divisor */ - unsigned short Reserved1:2; /* 0: reserved */ - unsigned short M_Multiplier:6; /* RW: (M) PLL feedback clock multiplier */ - unsigned short Reserved2:2; /* 0: reserved */ -} DSP_CLOCK_CONTROL_1; - -typedef struct { - unsigned short PllBypass:1; /* RW: PLL Bypass */ - unsigned short Reserved:15; /* 0: Reserved */ -} DSP_CLOCK_CONTROL_2; - -typedef struct { - unsigned short Latch8:1; - unsigned short Latch9:1; - unsigned short Latch10:1; - unsigned short Latch11:1; - unsigned short Latch12:1; - unsigned short Latch13:1; - unsigned short Latch14:1; - unsigned short Latch15:1; - unsigned short Mask8:1; - unsigned short Mask9:1; - unsigned short Mask10:1; - unsigned short Mask11:1; - unsigned short Mask12:1; - unsigned short Mask13:1; - unsigned short Mask14:1; - unsigned short Mask15:1; -} DSP_GPIO_OUTPUT_DATA_15_8; - -typedef struct { - unsigned short Enable8:1; - unsigned short Enable9:1; - unsigned short Enable10:1; - unsigned short Enable11:1; - unsigned short Enable12:1; - unsigned short Enable13:1; - unsigned short Enable14:1; - unsigned short Enable15:1; - unsigned short Mask8:1; - unsigned short Mask9:1; - unsigned short Mask10:1; - unsigned short Mask11:1; - unsigned short Mask12:1; - unsigned short Mask13:1; - unsigned short Mask14:1; - unsigned short Mask15:1; -} DSP_GPIO_DRIVER_ENABLE_15_8; - -typedef struct { - unsigned short GpioMode8:2; - unsigned short GpioMode9:2; - unsigned short GpioMode10:2; - unsigned short GpioMode11:2; - unsigned short GpioMode12:2; - unsigned short GpioMode13:2; - unsigned short GpioMode14:2; - unsigned short GpioMode15:2; -} DSP_GPIO_MODE_15_8; - -/* Component masks that are defined in dspmgr.h */ -#define MW_ADC_MASK 0x0001 -#define MW_AIC2_MASK 0x0006 -#define MW_MIDI_MASK 0x0008 -#define MW_CDDAC_MASK 0x8001 -#define MW_AIC1_MASK 0xE006 -#define MW_UART_MASK 0xE00A -#define MW_ACI_MASK 0xE00B - -/* -* Definition of 3780i configuration structure. Unless otherwise stated, -* these values are provided as input to the 3780i support layer. At present, -* the only values maintained by the 3780i support layer are the saved UART -* registers. -*/ -typedef struct _DSP_3780I_CONFIG_SETTINGS { - - /* Location of base configuration register */ - unsigned short usBaseConfigIO; - - /* Enables for various DSP components */ - int bDSPEnabled; - int bModemEnabled; - int bInterruptClaimed; - - /* IRQ, DMA, and Base I/O addresses for various DSP components */ - unsigned short usDspIrq; - unsigned short usDspDma; - unsigned short usDspBaseIO; - unsigned short usUartIrq; - unsigned short usUartBaseIO; - - /* IRQ modes for various DSP components */ - int bDspIrqActiveLow; - int bUartIrqActiveLow; - int bDspIrqPulse; - int bUartIrqPulse; - - /* Card abilities */ - unsigned uIps; - unsigned uDStoreSize; - unsigned uIStoreSize; - unsigned uDmaBandwidth; - - /* Adapter specific 3780i settings */ - unsigned short usNumTransfers; - unsigned short usReRequest; - int bEnableMEMCS16; - unsigned short usIsaMemCmdWidth; - int bGateIOCHRDY; - int bEnablePwrMgmt; - unsigned short usHBusTimerLoadValue; - int bDisableLBusTimeout; - unsigned short usN_Divisor; - unsigned short usM_Multiplier; - int bPllBypass; - unsigned short usChipletEnable; /* Used with the chip reset register to enable specific chiplets */ - - /* Saved UART registers. These are maintained by the 3780i support layer. */ - int bUartSaved; /* True after a successful save of the UART registers */ - unsigned char ucIER; /* Interrupt enable register */ - unsigned char ucFCR; /* FIFO control register */ - unsigned char ucLCR; /* Line control register */ - unsigned char ucMCR; /* Modem control register */ - unsigned char ucSCR; /* Scratch register */ - unsigned char ucDLL; /* Divisor latch, low byte */ - unsigned char ucDLM; /* Divisor latch, high byte */ -} DSP_3780I_CONFIG_SETTINGS; - - -/* 3780i support functions */ -int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings, - unsigned short *pIrqMap, - unsigned short *pDmaMap); -int dsp3780I_DisableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings); -int dsp3780I_Reset(DSP_3780I_CONFIG_SETTINGS * pSettings); -int dsp3780I_Run(DSP_3780I_CONFIG_SETTINGS * pSettings); -int dsp3780I_ReadDStore(unsigned short usDspBaseIO, void __user *pvBuffer, - unsigned uCount, unsigned long ulDSPAddr); -int dsp3780I_ReadAndClearDStore(unsigned short usDspBaseIO, - void __user *pvBuffer, unsigned uCount, - unsigned long ulDSPAddr); -int dsp3780I_WriteDStore(unsigned short usDspBaseIO, void __user *pvBuffer, - unsigned uCount, unsigned long ulDSPAddr); -int dsp3780I_ReadIStore(unsigned short usDspBaseIO, void __user *pvBuffer, - unsigned uCount, unsigned long ulDSPAddr); -int dsp3780I_WriteIStore(unsigned short usDspBaseIO, void __user *pvBuffer, - unsigned uCount, unsigned long ulDSPAddr); -unsigned short dsp3780I_ReadMsaCfg(unsigned short usDspBaseIO, - unsigned long ulMsaAddr); -void dsp3780I_WriteMsaCfg(unsigned short usDspBaseIO, - unsigned long ulMsaAddr, unsigned short usValue); -int dsp3780I_GetIPCSource(unsigned short usDspBaseIO, - unsigned short *pusIPCSource); - -/* I/O port access macros */ -#define MKWORD(var) (*((unsigned short *)(&var))) -#define MKBYTE(var) (*((unsigned char *)(&var))) - -#define WriteMsaCfg(addr,value) dsp3780I_WriteMsaCfg(usDspBaseIO,addr,value) -#define ReadMsaCfg(addr) dsp3780I_ReadMsaCfg(usDspBaseIO,addr) -#define WriteGenCfg(index,value) dsp3780I_WriteGenCfg(usDspBaseIO,index,value) -#define ReadGenCfg(index) dsp3780I_ReadGenCfg(usDspBaseIO,index) - -#define InWordDsp(index) inw(usDspBaseIO+index) -#define InByteDsp(index) inb(usDspBaseIO+index) -#define OutWordDsp(index,value) outw(value,usDspBaseIO+index) -#define OutByteDsp(index,value) outb(value,usDspBaseIO+index) - -#endif diff --git a/drivers/char/mwave/Makefile b/drivers/char/mwave/Makefile deleted file mode 100644 index a24fe96e3c96..000000000000 --- a/drivers/char/mwave/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for ACP Modem (Mwave). -# -# See the README file in this directory for more info. <paulsch@us.ibm.com> -# - -obj-$(CONFIG_MWAVE) += mwave.o - -mwave-y := mwavedd.o smapi.o tp3780i.o 3780i.o - -# To have the mwave driver disable other uarts if necessary -# ccflags-y := -DMWAVE_FUTZ_WITH_OTHER_DEVICES - -# To compile in lots (~20 KiB) of run-time enablable printk()s for debugging: -ccflags-y += -DMW_TRACE diff --git a/drivers/char/mwave/README b/drivers/char/mwave/README deleted file mode 100644 index c2a58f428bc8..000000000000 --- a/drivers/char/mwave/README +++ /dev/null @@ -1,47 +0,0 @@ -Module options --------------- - -The mwave module takes the following options. Note that these options -are not saved by the BIOS and so do not persist after unload and reload. - - mwave_debug=value, where value is bitwise OR of trace flags: - 0x0001 mwavedd api tracing - 0x0002 smapi api tracing - 0x0004 3780i tracing - 0x0008 tp3780i tracing - - Tracing only occurs if the driver has been compiled with the - MW_TRACE macro #defined (i.e. let ccflags-y := -DMW_TRACE - in the Makefile). - - mwave_3780i_irq=5/7/10/11/15 - If the dsp irq has not been setup and stored in bios by the - thinkpad configuration utility then this parameter allows the - irq used by the dsp to be configured. - - mwave_3780i_io=0x130/0x350/0x0070/0xDB0 - If the dsp io range has not been setup and stored in bios by the - thinkpad configuration utility then this parameter allows the - io range used by the dsp to be configured. - - mwave_uart_irq=3/4 - If the mwave's uart irq has not been setup and stored in bios by the - thinkpad configuration utility then this parameter allows the - irq used by the mwave uart to be configured. - - mwave_uart_io=0x3f8/0x2f8/0x3E8/0x2E8 - If the uart io range has not been setup and stored in bios by the - thinkpad configuration utility then this parameter allows the - io range used by the mwave uart to be configured. - -Example to enable the 3780i DSP using ttyS1 resources: - - insmod mwave mwave_3780i_irq=10 mwave_3780i_io=0x0130 mwave_uart_irq=3 mwave_uart_io=0x2f8 - -Accessing the driver --------------------- - -You must also create a node for the driver: - mkdir -p /dev/modems - mknod --mode=660 /dev/modems/mwave c 10 219 - diff --git a/drivers/char/mwave/mwavedd.c b/drivers/char/mwave/mwavedd.c deleted file mode 100644 index 11272d605ecd..000000000000 --- a/drivers/char/mwave/mwavedd.c +++ /dev/null @@ -1,703 +0,0 @@ -/* -* -* mwavedd.c -- mwave device driver -* -* -* Written By: Mike Sullivan IBM Corporation -* -* Copyright (C) 1999 IBM Corporation -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* NO WARRANTY -* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR -* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT -* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, -* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is -* solely responsible for determining the appropriateness of using and -* distributing the Program and assumes all risks associated with its -* exercise of rights under this Agreement, including but not limited to -* the risks and costs of program errors, damage to or loss of data, -* programs or equipment, and unavailability or interruption of operations. -* -* DISCLAIMER OF LIABILITY -* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* -* 10/23/2000 - Alpha Release -* First release to the public -*/ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/fs.h> -#include <linux/init.h> -#include <linux/major.h> -#include <linux/miscdevice.h> -#include <linux/device.h> -#include <linux/serial.h> -#include <linux/sched.h> -#include <linux/spinlock.h> -#include <linux/mutex.h> -#include <linux/delay.h> -#include <linux/serial_8250.h> -#include <linux/nospec.h> -#include "smapi.h" -#include "mwavedd.h" -#include "3780i.h" -#include "tp3780i.h" - -MODULE_DESCRIPTION("3780i Advanced Communications Processor (Mwave) driver"); -MODULE_AUTHOR("Mike Sullivan and Paul Schroeder"); -MODULE_LICENSE("GPL"); - -/* -* These parameters support the setting of MWave resources. Note that no -* checks are made against other devices (ie. superio) for conflicts. -* We'll depend on users using the tpctl utility to do that for now -*/ -static DEFINE_MUTEX(mwave_mutex); -int mwave_debug = 0; -int mwave_3780i_irq = 0; -int mwave_3780i_io = 0; -int mwave_uart_irq = 0; -int mwave_uart_io = 0; -module_param(mwave_debug, int, 0); -module_param_hw(mwave_3780i_irq, int, irq, 0); -module_param_hw(mwave_3780i_io, int, ioport, 0); -module_param_hw(mwave_uart_irq, int, irq, 0); -module_param_hw(mwave_uart_io, int, ioport, 0); - -static int mwave_open(struct inode *inode, struct file *file); -static int mwave_close(struct inode *inode, struct file *file); -static long mwave_ioctl(struct file *filp, unsigned int iocmd, - unsigned long ioarg); - -MWAVE_DEVICE_DATA mwave_s_mdd; - -static int mwave_open(struct inode *inode, struct file *file) -{ - unsigned int retval = 0; - - PRINTK_3(TRACE_MWAVE, - "mwavedd::mwave_open, entry inode %p file %p\n", - inode, file); - PRINTK_2(TRACE_MWAVE, - "mwavedd::mwave_open, exit return retval %x\n", retval); - - return retval; -} - -static int mwave_close(struct inode *inode, struct file *file) -{ - unsigned int retval = 0; - - PRINTK_3(TRACE_MWAVE, - "mwavedd::mwave_close, entry inode %p file %p\n", - inode, file); - - PRINTK_2(TRACE_MWAVE, "mwavedd::mwave_close, exit retval %x\n", - retval); - - return retval; -} - -static long mwave_ioctl(struct file *file, unsigned int iocmd, - unsigned long ioarg) -{ - unsigned int retval = 0; - pMWAVE_DEVICE_DATA pDrvData = &mwave_s_mdd; - void __user *arg = (void __user *)ioarg; - - PRINTK_4(TRACE_MWAVE, - "mwavedd::mwave_ioctl, entry file %p cmd %x arg %x\n", - file, iocmd, (int) ioarg); - - switch (iocmd) { - - case IOCTL_MW_RESET: - PRINTK_1(TRACE_MWAVE, - "mwavedd::mwave_ioctl, IOCTL_MW_RESET" - " calling tp3780I_ResetDSP\n"); - mutex_lock(&mwave_mutex); - retval = tp3780I_ResetDSP(&pDrvData->rBDData); - mutex_unlock(&mwave_mutex); - PRINTK_2(TRACE_MWAVE, - "mwavedd::mwave_ioctl, IOCTL_MW_RESET" - " retval %x from tp3780I_ResetDSP\n", - retval); - break; - - case IOCTL_MW_RUN: - PRINTK_1(TRACE_MWAVE, - "mwavedd::mwave_ioctl, IOCTL_MW_RUN" - " calling tp3780I_StartDSP\n"); - mutex_lock(&mwave_mutex); - retval = tp3780I_StartDSP(&pDrvData->rBDData); - mutex_unlock(&mwave_mutex); - PRINTK_2(TRACE_MWAVE, - "mwavedd::mwave_ioctl, IOCTL_MW_RUN" - " retval %x from tp3780I_StartDSP\n", - retval); - break; - - case IOCTL_MW_DSP_ABILITIES: { - MW_ABILITIES rAbilities; - - PRINTK_1(TRACE_MWAVE, - "mwavedd::mwave_ioctl," - " IOCTL_MW_DSP_ABILITIES calling" - " tp3780I_QueryAbilities\n"); - mutex_lock(&mwave_mutex); - retval = tp3780I_QueryAbilities(&pDrvData->rBDData, - &rAbilities); - mutex_unlock(&mwave_mutex); - PRINTK_2(TRACE_MWAVE, - "mwavedd::mwave_ioctl, IOCTL_MW_DSP_ABILITIES" - " retval %x from tp3780I_QueryAbilities\n", - retval); - if (retval == 0) { - if( copy_to_user(arg, &rAbilities, - sizeof(MW_ABILITIES)) ) - return -EFAULT; - } - PRINTK_2(TRACE_MWAVE, - "mwavedd::mwave_ioctl, IOCTL_MW_DSP_ABILITIES" - " exit retval %x\n", - retval); - } - break; - - case IOCTL_MW_READ_DATA: - case IOCTL_MW_READCLEAR_DATA: { - MW_READWRITE rReadData; - unsigned short __user *pusBuffer = NULL; - - if( copy_from_user(&rReadData, arg, - sizeof(MW_READWRITE)) ) - return -EFAULT; - pusBuffer = (unsigned short __user *) (rReadData.pBuf); - - PRINTK_4(TRACE_MWAVE, - "mwavedd::mwave_ioctl IOCTL_MW_READ_DATA," - " size %lx, ioarg %lx pusBuffer %p\n", - rReadData.ulDataLength, ioarg, pusBuffer); - mutex_lock(&mwave_mutex); - retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData, - iocmd, - pusBuffer, - rReadData.ulDataLength, - rReadData.usDspAddress); - mutex_unlock(&mwave_mutex); - } - break; - - case IOCTL_MW_READ_INST: { - MW_READWRITE rReadData; - unsigned short __user *pusBuffer = NULL; - - if( copy_from_user(&rReadData, arg, - sizeof(MW_READWRITE)) ) - return -EFAULT; - pusBuffer = (unsigned short __user *) (rReadData.pBuf); - - PRINTK_4(TRACE_MWAVE, - "mwavedd::mwave_ioctl IOCTL_MW_READ_INST," - " size %lx, ioarg %lx pusBuffer %p\n", - rReadData.ulDataLength / 2, ioarg, - pusBuffer); - mutex_lock(&mwave_mutex); - retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData, - iocmd, pusBuffer, - rReadData.ulDataLength / 2, - rReadData.usDspAddress); - mutex_unlock(&mwave_mutex); - } - break; - - case IOCTL_MW_WRITE_DATA: { - MW_READWRITE rWriteData; - unsigned short __user *pusBuffer = NULL; - - if( copy_from_user(&rWriteData, arg, - sizeof(MW_READWRITE)) ) - return -EFAULT; - pusBuffer = (unsigned short __user *) (rWriteData.pBuf); - - PRINTK_4(TRACE_MWAVE, - "mwavedd::mwave_ioctl IOCTL_MW_WRITE_DATA," - " size %lx, ioarg %lx pusBuffer %p\n", - rWriteData.ulDataLength, ioarg, - pusBuffer); - mutex_lock(&mwave_mutex); - retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData, - iocmd, pusBuffer, - rWriteData.ulDataLength, - rWriteData.usDspAddress); - mutex_unlock(&mwave_mutex); - } - break; - - case IOCTL_MW_WRITE_INST: { - MW_READWRITE rWriteData; - unsigned short __user *pusBuffer = NULL; - - if( copy_from_user(&rWriteData, arg, - sizeof(MW_READWRITE)) ) - return -EFAULT; - pusBuffer = (unsigned short __user *)(rWriteData.pBuf); - - PRINTK_4(TRACE_MWAVE, - "mwavedd::mwave_ioctl IOCTL_MW_WRITE_INST," - " size %lx, ioarg %lx pusBuffer %p\n", - rWriteData.ulDataLength, ioarg, - pusBuffer); - mutex_lock(&mwave_mutex); - retval = tp3780I_ReadWriteDspIStore(&pDrvData->rBDData, - iocmd, pusBuffer, - rWriteData.ulDataLength, - rWriteData.usDspAddress); - mutex_unlock(&mwave_mutex); - } - break; - - case IOCTL_MW_REGISTER_IPC: { - unsigned int ipcnum = (unsigned int) ioarg; - - if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) { - PRINTK_ERROR(KERN_ERR_MWAVE - "mwavedd::mwave_ioctl:" - " IOCTL_MW_REGISTER_IPC:" - " Error: Invalid ipcnum %x\n", - ipcnum); - return -EINVAL; - } - ipcnum = array_index_nospec(ipcnum, - ARRAY_SIZE(pDrvData->IPCs)); - PRINTK_3(TRACE_MWAVE, - "mwavedd::mwave_ioctl IOCTL_MW_REGISTER_IPC" - " ipcnum %x entry usIntCount %x\n", - ipcnum, - pDrvData->IPCs[ipcnum].usIntCount); - - mutex_lock(&mwave_mutex); - pDrvData->IPCs[ipcnum].bIsHere = false; - pDrvData->IPCs[ipcnum].bIsEnabled = true; - mutex_unlock(&mwave_mutex); - - PRINTK_2(TRACE_MWAVE, - "mwavedd::mwave_ioctl IOCTL_MW_REGISTER_IPC" - " ipcnum %x exit\n", - ipcnum); - } - break; - - case IOCTL_MW_GET_IPC: { - unsigned int ipcnum = (unsigned int) ioarg; - - if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) { - PRINTK_ERROR(KERN_ERR_MWAVE - "mwavedd::mwave_ioctl:" - " IOCTL_MW_GET_IPC: Error:" - " Invalid ipcnum %x\n", ipcnum); - return -EINVAL; - } - ipcnum = array_index_nospec(ipcnum, - ARRAY_SIZE(pDrvData->IPCs)); - PRINTK_3(TRACE_MWAVE, - "mwavedd::mwave_ioctl IOCTL_MW_GET_IPC" - " ipcnum %x, usIntCount %x\n", - ipcnum, - pDrvData->IPCs[ipcnum].usIntCount); - - mutex_lock(&mwave_mutex); - if (pDrvData->IPCs[ipcnum].bIsEnabled == true) { - DECLARE_WAITQUEUE(wait, current); - - PRINTK_2(TRACE_MWAVE, - "mwavedd::mwave_ioctl, thread for" - " ipc %x going to sleep\n", - ipcnum); - add_wait_queue(&pDrvData->IPCs[ipcnum].ipc_wait_queue, &wait); - pDrvData->IPCs[ipcnum].bIsHere = true; - set_current_state(TASK_INTERRUPTIBLE); - /* check whether an event was signalled by */ - /* the interrupt handler while we were gone */ - if (pDrvData->IPCs[ipcnum].usIntCount == 1) { /* first int has occurred (race condition) */ - pDrvData->IPCs[ipcnum].usIntCount = 2; /* first int has been handled */ - PRINTK_2(TRACE_MWAVE, - "mwavedd::mwave_ioctl" - " IOCTL_MW_GET_IPC ipcnum %x" - " handling first int\n", - ipcnum); - } else { /* either 1st int has not yet occurred, or we have already handled the first int */ - schedule(); - if (pDrvData->IPCs[ipcnum].usIntCount == 1) { - pDrvData->IPCs[ipcnum].usIntCount = 2; - } - PRINTK_2(TRACE_MWAVE, - "mwavedd::mwave_ioctl" - " IOCTL_MW_GET_IPC ipcnum %x" - " woke up and returning to" - " application\n", - ipcnum); - } - pDrvData->IPCs[ipcnum].bIsHere = false; - remove_wait_queue(&pDrvData->IPCs[ipcnum].ipc_wait_queue, &wait); - set_current_state(TASK_RUNNING); - PRINTK_2(TRACE_MWAVE, - "mwavedd::mwave_ioctl IOCTL_MW_GET_IPC," - " returning thread for ipc %x" - " processing\n", - ipcnum); - } - mutex_unlock(&mwave_mutex); - } - break; - - case IOCTL_MW_UNREGISTER_IPC: { - unsigned int ipcnum = (unsigned int) ioarg; - - PRINTK_2(TRACE_MWAVE, - "mwavedd::mwave_ioctl IOCTL_MW_UNREGISTER_IPC" - " ipcnum %x\n", - ipcnum); - if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) { - PRINTK_ERROR(KERN_ERR_MWAVE - "mwavedd::mwave_ioctl:" - " IOCTL_MW_UNREGISTER_IPC:" - " Error: Invalid ipcnum %x\n", - ipcnum); - return -EINVAL; - } - ipcnum = array_index_nospec(ipcnum, - ARRAY_SIZE(pDrvData->IPCs)); - mutex_lock(&mwave_mutex); - if (pDrvData->IPCs[ipcnum].bIsEnabled == true) { - pDrvData->IPCs[ipcnum].bIsEnabled = false; - if (pDrvData->IPCs[ipcnum].bIsHere == true) { - wake_up_interruptible(&pDrvData->IPCs[ipcnum].ipc_wait_queue); - } - } - mutex_unlock(&mwave_mutex); - } - break; - - default: - return -ENOTTY; - } /* switch */ - - PRINTK_2(TRACE_MWAVE, "mwavedd::mwave_ioctl, exit retval %x\n", retval); - - return retval; -} - - -static ssize_t mwave_read(struct file *file, char __user *buf, size_t count, - loff_t * ppos) -{ - PRINTK_5(TRACE_MWAVE, - "mwavedd::mwave_read entry file %p, buf %p, count %zx ppos %p\n", - file, buf, count, ppos); - - return -EINVAL; -} - - -static ssize_t mwave_write(struct file *file, const char __user *buf, - size_t count, loff_t * ppos) -{ - PRINTK_5(TRACE_MWAVE, - "mwavedd::mwave_write entry file %p, buf %p," - " count %zx ppos %p\n", - file, buf, count, ppos); - - return -EINVAL; -} - - -static int register_serial_portandirq(unsigned int port, int irq) -{ - struct uart_8250_port uart; - - switch ( port ) { - case 0x3f8: - case 0x2f8: - case 0x3e8: - case 0x2e8: - /* OK */ - break; - default: - PRINTK_ERROR(KERN_ERR_MWAVE - "mwavedd::register_serial_portandirq:" - " Error: Illegal port %x\n", port ); - return -1; - } /* switch */ - /* port is okay */ - - switch ( irq ) { - case 3: - case 4: - case 5: - case 7: - /* OK */ - break; - default: - PRINTK_ERROR(KERN_ERR_MWAVE - "mwavedd::register_serial_portandirq:" - " Error: Illegal irq %x\n", irq ); - return -1; - } /* switch */ - /* irq is okay */ - - memset(&uart, 0, sizeof(uart)); - - uart.port.uartclk = 1843200; - uart.port.iobase = port; - uart.port.irq = irq; - uart.port.iotype = UPIO_PORT; - uart.port.flags = UPF_SHARE_IRQ; - return serial8250_register_8250_port(&uart); -} - - -static const struct file_operations mwave_fops = { - .owner = THIS_MODULE, - .read = mwave_read, - .write = mwave_write, - .unlocked_ioctl = mwave_ioctl, - .open = mwave_open, - .release = mwave_close, - .llseek = default_llseek, -}; - - -static struct miscdevice mwave_misc_dev = { MWAVE_MINOR, "mwave", &mwave_fops }; - -#if 0 /* totally b0rked */ -/* - * sysfs support <paulsch@us.ibm.com> - */ - -struct device mwave_device; - -/* Prevent code redundancy, create a macro for mwave_show_* functions. */ -#define mwave_show_function(attr_name, format_string, field) \ -static ssize_t mwave_show_##attr_name(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - DSP_3780I_CONFIG_SETTINGS *pSettings = \ - &mwave_s_mdd.rBDData.rDspSettings; \ - return sprintf(buf, format_string, pSettings->field); \ -} - -/* All of our attributes are read attributes. */ -#define mwave_dev_rd_attr(attr_name, format_string, field) \ - mwave_show_function(attr_name, format_string, field) \ -static DEVICE_ATTR(attr_name, S_IRUGO, mwave_show_##attr_name, NULL) - -mwave_dev_rd_attr (3780i_dma, "%i\n", usDspDma); -mwave_dev_rd_attr (3780i_irq, "%i\n", usDspIrq); -mwave_dev_rd_attr (3780i_io, "%#.4x\n", usDspBaseIO); -mwave_dev_rd_attr (uart_irq, "%i\n", usUartIrq); -mwave_dev_rd_attr (uart_io, "%#.4x\n", usUartBaseIO); - -static struct device_attribute * const mwave_dev_attrs[] = { - &dev_attr_3780i_dma, - &dev_attr_3780i_irq, - &dev_attr_3780i_io, - &dev_attr_uart_irq, - &dev_attr_uart_io, -}; -#endif - -/* -* mwave_init is called on module load -* -* mwave_exit is called on module unload -* mwave_exit is also used to clean up after an aborted mwave_init -*/ -static void mwave_exit(void) -{ - pMWAVE_DEVICE_DATA pDrvData = &mwave_s_mdd; - - PRINTK_1(TRACE_MWAVE, "mwavedd::mwave_exit entry\n"); - -#if 0 - for (i = 0; i < pDrvData->nr_registered_attrs; i++) - device_remove_file(&mwave_device, mwave_dev_attrs[i]); - pDrvData->nr_registered_attrs = 0; - - if (pDrvData->device_registered) { - device_unregister(&mwave_device); - pDrvData->device_registered = false; - } -#endif - - if ( pDrvData->sLine >= 0 ) { - serial8250_unregister_port(pDrvData->sLine); - } - if (pDrvData->bMwaveDevRegistered) { - misc_deregister(&mwave_misc_dev); - } - if (pDrvData->bDSPEnabled) { - tp3780I_DisableDSP(&pDrvData->rBDData); - } - if (pDrvData->bResourcesClaimed) { - tp3780I_ReleaseResources(&pDrvData->rBDData); - } - if (pDrvData->bBDInitialized) { - tp3780I_Cleanup(&pDrvData->rBDData); - } - - PRINTK_1(TRACE_MWAVE, "mwavedd::mwave_exit exit\n"); -} - -module_exit(mwave_exit); - -static int __init mwave_init(void) -{ - int i; - int retval = 0; - pMWAVE_DEVICE_DATA pDrvData = &mwave_s_mdd; - - PRINTK_1(TRACE_MWAVE, "mwavedd::mwave_init entry\n"); - - memset(&mwave_s_mdd, 0, sizeof(MWAVE_DEVICE_DATA)); - - pDrvData->bBDInitialized = false; - pDrvData->bResourcesClaimed = false; - pDrvData->bDSPEnabled = false; - pDrvData->bDSPReset = false; - pDrvData->bMwaveDevRegistered = false; - pDrvData->sLine = -1; - - for (i = 0; i < ARRAY_SIZE(pDrvData->IPCs); i++) { - pDrvData->IPCs[i].bIsEnabled = false; - pDrvData->IPCs[i].bIsHere = false; - pDrvData->IPCs[i].usIntCount = 0; /* no ints received yet */ - init_waitqueue_head(&pDrvData->IPCs[i].ipc_wait_queue); - } - - retval = tp3780I_InitializeBoardData(&pDrvData->rBDData); - PRINTK_2(TRACE_MWAVE, - "mwavedd::mwave_init, return from tp3780I_InitializeBoardData" - " retval %x\n", - retval); - if (retval) { - PRINTK_ERROR(KERN_ERR_MWAVE - "mwavedd::mwave_init: Error:" - " Failed to initialize board data\n"); - goto cleanup_error; - } - pDrvData->bBDInitialized = true; - - retval = tp3780I_CalcResources(&pDrvData->rBDData); - PRINTK_2(TRACE_MWAVE, - "mwavedd::mwave_init, return from tp3780I_CalcResources" - " retval %x\n", - retval); - if (retval) { - PRINTK_ERROR(KERN_ERR_MWAVE - "mwavedd:mwave_init: Error:" - " Failed to calculate resources\n"); - goto cleanup_error; - } - - retval = tp3780I_ClaimResources(&pDrvData->rBDData); - PRINTK_2(TRACE_MWAVE, - "mwavedd::mwave_init, return from tp3780I_ClaimResources" - " retval %x\n", - retval); - if (retval) { - PRINTK_ERROR(KERN_ERR_MWAVE - "mwavedd:mwave_init: Error:" - " Failed to claim resources\n"); - goto cleanup_error; - } - pDrvData->bResourcesClaimed = true; - - retval = tp3780I_EnableDSP(&pDrvData->rBDData); - PRINTK_2(TRACE_MWAVE, - "mwavedd::mwave_init, return from tp3780I_EnableDSP" - " retval %x\n", - retval); - if (retval) { - PRINTK_ERROR(KERN_ERR_MWAVE - "mwavedd:mwave_init: Error:" - " Failed to enable DSP\n"); - goto cleanup_error; - } - pDrvData->bDSPEnabled = true; - - if (misc_register(&mwave_misc_dev) < 0) { - PRINTK_ERROR(KERN_ERR_MWAVE - "mwavedd:mwave_init: Error:" - " Failed to register misc device\n"); - goto cleanup_error; - } - pDrvData->bMwaveDevRegistered = true; - - pDrvData->sLine = register_serial_portandirq( - pDrvData->rBDData.rDspSettings.usUartBaseIO, - pDrvData->rBDData.rDspSettings.usUartIrq - ); - if (pDrvData->sLine < 0) { - PRINTK_ERROR(KERN_ERR_MWAVE - "mwavedd:mwave_init: Error:" - " Failed to register serial driver\n"); - goto cleanup_error; - } - /* uart is registered */ - -#if 0 - /* sysfs */ - memset(&mwave_device, 0, sizeof (struct device)); - dev_set_name(&mwave_device, "mwave"); - - if (device_register(&mwave_device)) - goto cleanup_error; - pDrvData->device_registered = true; - for (i = 0; i < ARRAY_SIZE(mwave_dev_attrs); i++) { - if(device_create_file(&mwave_device, mwave_dev_attrs[i])) { - PRINTK_ERROR(KERN_ERR_MWAVE - "mwavedd:mwave_init: Error:" - " Failed to create sysfs file %s\n", - mwave_dev_attrs[i]->attr.name); - goto cleanup_error; - } - pDrvData->nr_registered_attrs++; - } -#endif - - /* SUCCESS! */ - return 0; - -cleanup_error: - PRINTK_ERROR(KERN_ERR_MWAVE - "mwavedd::mwave_init: Error:" - " Failed to initialize\n"); - mwave_exit(); /* clean up */ - - return -EIO; -} - -module_init(mwave_init); - diff --git a/drivers/char/mwave/mwavedd.h b/drivers/char/mwave/mwavedd.h deleted file mode 100644 index 21cb09c7bed7..000000000000 --- a/drivers/char/mwave/mwavedd.h +++ /dev/null @@ -1,152 +0,0 @@ -/* -* -* mwavedd.h -- declarations for mwave device driver -* -* -* Written By: Mike Sullivan IBM Corporation -* -* Copyright (C) 1999 IBM Corporation -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* NO WARRANTY -* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR -* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT -* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, -* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is -* solely responsible for determining the appropriateness of using and -* distributing the Program and assumes all risks associated with its -* exercise of rights under this Agreement, including but not limited to -* the risks and costs of program errors, damage to or loss of data, -* programs or equipment, and unavailability or interruption of operations. -* -* DISCLAIMER OF LIABILITY -* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* -* 10/23/2000 - Alpha Release -* First release to the public -*/ - -#ifndef _LINUX_MWAVEDD_H -#define _LINUX_MWAVEDD_H -#include "3780i.h" -#include "tp3780i.h" -#include "smapi.h" -#include "mwavepub.h" -#include <linux/ioctl.h> -#include <linux/uaccess.h> -#include <linux/wait.h> - -extern int mwave_debug; -extern int mwave_3780i_irq; -extern int mwave_3780i_io; -extern int mwave_uart_irq; -extern int mwave_uart_io; - -#define PRINTK_ERROR printk -#define KERN_ERR_MWAVE KERN_ERR "mwave: " - -#define TRACE_MWAVE 0x0001 -#define TRACE_SMAPI 0x0002 -#define TRACE_3780I 0x0004 -#define TRACE_TP3780I 0x0008 - -#ifdef MW_TRACE -#define PRINTK_1(f,s) \ - if (f & (mwave_debug)) { \ - printk(s); \ - } - -#define PRINTK_2(f,s,v1) \ - if (f & (mwave_debug)) { \ - printk(s,v1); \ - } - -#define PRINTK_3(f,s,v1,v2) \ - if (f & (mwave_debug)) { \ - printk(s,v1,v2); \ - } - -#define PRINTK_4(f,s,v1,v2,v3) \ - if (f & (mwave_debug)) { \ - printk(s,v1,v2,v3); \ - } - -#define PRINTK_5(f,s,v1,v2,v3,v4) \ - if (f & (mwave_debug)) { \ - printk(s,v1,v2,v3,v4); \ - } - -#define PRINTK_6(f,s,v1,v2,v3,v4,v5) \ - if (f & (mwave_debug)) { \ - printk(s,v1,v2,v3,v4,v5); \ - } - -#define PRINTK_7(f,s,v1,v2,v3,v4,v5,v6) \ - if (f & (mwave_debug)) { \ - printk(s,v1,v2,v3,v4,v5,v6); \ - } - -#define PRINTK_8(f,s,v1,v2,v3,v4,v5,v6,v7) \ - if (f & (mwave_debug)) { \ - printk(s,v1,v2,v3,v4,v5,v6,v7); \ - } - -#else -#define PRINTK_1(f,s) -#define PRINTK_2(f,s,v1) -#define PRINTK_3(f,s,v1,v2) -#define PRINTK_4(f,s,v1,v2,v3) -#define PRINTK_5(f,s,v1,v2,v3,v4) -#define PRINTK_6(f,s,v1,v2,v3,v4,v5) -#define PRINTK_7(f,s,v1,v2,v3,v4,v5,v6) -#define PRINTK_8(f,s,v1,v2,v3,v4,v5,v6,v7) -#endif - - -typedef struct _MWAVE_IPC { - unsigned short usIntCount; /* 0=none, 1=first, 2=greater than 1st */ - bool bIsEnabled; - bool bIsHere; - /* entry spin lock */ - wait_queue_head_t ipc_wait_queue; -} MWAVE_IPC; - -typedef struct _MWAVE_DEVICE_DATA { - THINKPAD_BD_DATA rBDData; /* board driver's data area */ - unsigned long ulIPCSource_ISR; /* IPC source bits for recently processed intr, set during ISR processing */ - unsigned long ulIPCSource_DPC; /* IPC source bits for recently processed intr, set during DPC processing */ - bool bBDInitialized; - bool bResourcesClaimed; - bool bDSPEnabled; - bool bDSPReset; - MWAVE_IPC IPCs[16]; - bool bMwaveDevRegistered; - short sLine; - int nr_registered_attrs; - int device_registered; - -} MWAVE_DEVICE_DATA, *pMWAVE_DEVICE_DATA; - -extern MWAVE_DEVICE_DATA mwave_s_mdd; - -#endif diff --git a/drivers/char/mwave/mwavepub.h b/drivers/char/mwave/mwavepub.h deleted file mode 100644 index 60c961ae23b4..000000000000 --- a/drivers/char/mwave/mwavepub.h +++ /dev/null @@ -1,89 +0,0 @@ -/* -* -* mwavepub.h -- PUBLIC declarations for the mwave driver -* and applications using it -* -* -* Written By: Mike Sullivan IBM Corporation -* -* Copyright (C) 1999 IBM Corporation -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* NO WARRANTY -* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR -* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT -* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, -* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is -* solely responsible for determining the appropriateness of using and -* distributing the Program and assumes all risks associated with its -* exercise of rights under this Agreement, including but not limited to -* the risks and costs of program errors, damage to or loss of data, -* programs or equipment, and unavailability or interruption of operations. -* -* DISCLAIMER OF LIABILITY -* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* -* 10/23/2000 - Alpha Release -* First release to the public -*/ - -#ifndef _LINUX_MWAVEPUB_H -#define _LINUX_MWAVEPUB_H - -#include <linux/miscdevice.h> - - -typedef struct _MW_ABILITIES { - unsigned long instr_per_sec; - unsigned long data_size; - unsigned long inst_size; - unsigned long bus_dma_bw; - unsigned short uart_enable; - short component_count; - unsigned long component_list[7]; - char mwave_os_name[16]; - char bios_task_name[16]; -} MW_ABILITIES, *pMW_ABILITIES; - - -typedef struct _MW_READWRITE { - unsigned short usDspAddress; /* The dsp address */ - unsigned long ulDataLength; /* The size in bytes of the data or user buffer */ - void __user *pBuf; /* Input:variable sized buffer */ -} MW_READWRITE, *pMW_READWRITE; - -#define IOCTL_MW_RESET _IO(MWAVE_MINOR,1) -#define IOCTL_MW_RUN _IO(MWAVE_MINOR,2) -#define IOCTL_MW_DSP_ABILITIES _IOR(MWAVE_MINOR,3,MW_ABILITIES) -#define IOCTL_MW_READ_DATA _IOR(MWAVE_MINOR,4,MW_READWRITE) -#define IOCTL_MW_READCLEAR_DATA _IOR(MWAVE_MINOR,5,MW_READWRITE) -#define IOCTL_MW_READ_INST _IOR(MWAVE_MINOR,6,MW_READWRITE) -#define IOCTL_MW_WRITE_DATA _IOW(MWAVE_MINOR,7,MW_READWRITE) -#define IOCTL_MW_WRITE_INST _IOW(MWAVE_MINOR,8,MW_READWRITE) -#define IOCTL_MW_REGISTER_IPC _IOW(MWAVE_MINOR,9,int) -#define IOCTL_MW_UNREGISTER_IPC _IOW(MWAVE_MINOR,10,int) -#define IOCTL_MW_GET_IPC _IOW(MWAVE_MINOR,11,int) -#define IOCTL_MW_TRACE _IOR(MWAVE_MINOR,12,MW_READWRITE) - - -#endif diff --git a/drivers/char/mwave/smapi.c b/drivers/char/mwave/smapi.c deleted file mode 100644 index f8d79d393b69..000000000000 --- a/drivers/char/mwave/smapi.c +++ /dev/null @@ -1,572 +0,0 @@ -/* -* -* smapi.c -- SMAPI interface routines -* -* -* Written By: Mike Sullivan IBM Corporation -* -* Copyright (C) 1999 IBM Corporation -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* NO WARRANTY -* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR -* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT -* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, -* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is -* solely responsible for determining the appropriateness of using and -* distributing the Program and assumes all risks associated with its -* exercise of rights under this Agreement, including but not limited to -* the risks and costs of program errors, damage to or loss of data, -* programs or equipment, and unavailability or interruption of operations. -* -* DISCLAIMER OF LIABILITY -* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* -* 10/23/2000 - Alpha Release -* First release to the public -*/ - -#include <linux/kernel.h> -#include <linux/mc146818rtc.h> /* CMOS defines */ -#include "smapi.h" -#include "mwavedd.h" - -static unsigned short g_usSmapiPort = 0; - - -static int smapi_request(unsigned short inBX, unsigned short inCX, - unsigned short inDI, unsigned short inSI, - unsigned short *outAX, unsigned short *outBX, - unsigned short *outCX, unsigned short *outDX, - unsigned short *outDI, unsigned short *outSI) -{ - unsigned short myoutAX = 2, *pmyoutAX = &myoutAX; - unsigned short myoutBX = 3, *pmyoutBX = &myoutBX; - unsigned short myoutCX = 4, *pmyoutCX = &myoutCX; - unsigned short myoutDX = 5, *pmyoutDX = &myoutDX; - unsigned short myoutDI = 6, *pmyoutDI = &myoutDI; - unsigned short myoutSI = 7, *pmyoutSI = &myoutSI; - unsigned short usSmapiOK = -EIO, *pusSmapiOK = &usSmapiOK; - unsigned int inBXCX = (inBX << 16) | inCX; - unsigned int inDISI = (inDI << 16) | inSI; - int retval = 0; - - PRINTK_5(TRACE_SMAPI, "inBX %x inCX %x inDI %x inSI %x\n", - inBX, inCX, inDI, inSI); - - __asm__ __volatile__("movw $0x5380,%%ax\n\t" - "movl %7,%%ebx\n\t" - "shrl $16, %%ebx\n\t" - "movw %7,%%cx\n\t" - "movl %8,%%edi\n\t" - "shrl $16,%%edi\n\t" - "movw %8,%%si\n\t" - "movw %9,%%dx\n\t" - "out %%al,%%dx\n\t" - "out %%al,$0x4F\n\t" - "cmpb $0x53,%%ah\n\t" - "je 2f\n\t" - "1:\n\t" - "orb %%ah,%%ah\n\t" - "jnz 2f\n\t" - "movw %%ax,%0\n\t" - "movw %%bx,%1\n\t" - "movw %%cx,%2\n\t" - "movw %%dx,%3\n\t" - "movw %%di,%4\n\t" - "movw %%si,%5\n\t" - "movw $1,%6\n\t" - "2:\n\t":"=m"(*(unsigned short *) pmyoutAX), - "=m"(*(unsigned short *) pmyoutBX), - "=m"(*(unsigned short *) pmyoutCX), - "=m"(*(unsigned short *) pmyoutDX), - "=m"(*(unsigned short *) pmyoutDI), - "=m"(*(unsigned short *) pmyoutSI), - "=m"(*(unsigned short *) pusSmapiOK) - :"m"(inBXCX), "m"(inDISI), "m"(g_usSmapiPort) - :"%eax", "%ebx", "%ecx", "%edx", "%edi", - "%esi"); - - PRINTK_8(TRACE_SMAPI, - "myoutAX %x myoutBX %x myoutCX %x myoutDX %x myoutDI %x myoutSI %x usSmapiOK %x\n", - myoutAX, myoutBX, myoutCX, myoutDX, myoutDI, myoutSI, - usSmapiOK); - *outAX = myoutAX; - *outBX = myoutBX; - *outCX = myoutCX; - *outDX = myoutDX; - *outDI = myoutDI; - *outSI = myoutSI; - - retval = (usSmapiOK == 1) ? 0 : -EIO; - PRINTK_2(TRACE_SMAPI, "smapi::smapi_request exit retval %x\n", retval); - return retval; -} - - -int smapi_query_DSP_cfg(SMAPI_DSP_SETTINGS * pSettings) -{ - int bRC; - unsigned short usAX, usBX, usCX, usDX, usDI, usSI; - static const unsigned short ausDspBases[] = { - 0x0030, 0x4E30, 0x8E30, 0xCE30, - 0x0130, 0x0350, 0x0070, 0x0DB0 }; - static const unsigned short ausUartBases[] = { - 0x03F8, 0x02F8, 0x03E8, 0x02E8 }; - - PRINTK_1(TRACE_SMAPI, "smapi::smapi_query_DSP_cfg entry\n"); - - bRC = smapi_request(0x1802, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) { - PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_query_DSP_cfg: Error: Could not get DSP Settings. Aborting.\n"); - return bRC; - } - - PRINTK_1(TRACE_SMAPI, "smapi::smapi_query_DSP_cfg, smapi_request OK\n"); - - pSettings->bDSPPresent = ((usBX & 0x0100) != 0); - pSettings->bDSPEnabled = ((usCX & 0x0001) != 0); - pSettings->usDspIRQ = usSI & 0x00FF; - pSettings->usDspDMA = (usSI & 0xFF00) >> 8; - if ((usDI & 0x00FF) < ARRAY_SIZE(ausDspBases)) { - pSettings->usDspBaseIO = ausDspBases[usDI & 0x00FF]; - } else { - pSettings->usDspBaseIO = 0; - } - PRINTK_6(TRACE_SMAPI, - "smapi::smapi_query_DSP_cfg get DSP Settings bDSPPresent %x bDSPEnabled %x usDspIRQ %x usDspDMA %x usDspBaseIO %x\n", - pSettings->bDSPPresent, pSettings->bDSPEnabled, - pSettings->usDspIRQ, pSettings->usDspDMA, - pSettings->usDspBaseIO); - - /* check for illegal values */ - if ( pSettings->usDspBaseIO == 0 ) - PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_query_DSP_cfg: Worry: DSP base I/O address is 0\n"); - if ( pSettings->usDspIRQ == 0 ) - PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_query_DSP_cfg: Worry: DSP IRQ line is 0\n"); - - bRC = smapi_request(0x1804, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) { - PRINTK_ERROR("smapi::smapi_query_DSP_cfg: Error: Could not get DSP modem settings. Aborting.\n"); - return bRC; - } - - PRINTK_1(TRACE_SMAPI, "smapi::smapi_query_DSP_cfg, smapi_request OK\n"); - - pSettings->bModemEnabled = ((usCX & 0x0001) != 0); - pSettings->usUartIRQ = usSI & 0x000F; - if (((usSI & 0xFF00) >> 8) < ARRAY_SIZE(ausUartBases)) { - pSettings->usUartBaseIO = ausUartBases[(usSI & 0xFF00) >> 8]; - } else { - pSettings->usUartBaseIO = 0; - } - - PRINTK_4(TRACE_SMAPI, - "smapi::smapi_query_DSP_cfg get DSP modem settings bModemEnabled %x usUartIRQ %x usUartBaseIO %x\n", - pSettings->bModemEnabled, - pSettings->usUartIRQ, - pSettings->usUartBaseIO); - - /* check for illegal values */ - if ( pSettings->usUartBaseIO == 0 ) - PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_query_DSP_cfg: Worry: UART base I/O address is 0\n"); - if ( pSettings->usUartIRQ == 0 ) - PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_query_DSP_cfg: Worry: UART IRQ line is 0\n"); - - PRINTK_2(TRACE_SMAPI, "smapi::smapi_query_DSP_cfg exit bRC %x\n", bRC); - - return bRC; -} - - -int smapi_set_DSP_cfg(void) -{ - int bRC = -EIO; - int i; - unsigned short usAX, usBX, usCX, usDX, usDI, usSI; - static const unsigned short ausDspBases[] = { - 0x0030, 0x4E30, 0x8E30, 0xCE30, - 0x0130, 0x0350, 0x0070, 0x0DB0 }; - static const unsigned short ausUartBases[] = { - 0x03F8, 0x02F8, 0x03E8, 0x02E8 }; - static const unsigned short ausDspIrqs[] = { - 5, 7, 10, 11, 15 }; - static const unsigned short ausUartIrqs[] = { - 3, 4 }; - - unsigned short dspio_index = 0, uartio_index = 0; - - PRINTK_5(TRACE_SMAPI, - "smapi::smapi_set_DSP_cfg entry mwave_3780i_irq %x mwave_3780i_io %x mwave_uart_irq %x mwave_uart_io %x\n", - mwave_3780i_irq, mwave_3780i_io, mwave_uart_irq, mwave_uart_io); - - if (mwave_3780i_io) { - for (i = 0; i < ARRAY_SIZE(ausDspBases); i++) { - if (mwave_3780i_io == ausDspBases[i]) - break; - } - if (i == ARRAY_SIZE(ausDspBases)) { - PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_set_DSP_cfg: Error: Invalid mwave_3780i_io address %x. Aborting.\n", mwave_3780i_io); - return bRC; - } - dspio_index = i; - } - - if (mwave_3780i_irq) { - for (i = 0; i < ARRAY_SIZE(ausDspIrqs); i++) { - if (mwave_3780i_irq == ausDspIrqs[i]) - break; - } - if (i == ARRAY_SIZE(ausDspIrqs)) { - PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_set_DSP_cfg: Error: Invalid mwave_3780i_irq %x. Aborting.\n", mwave_3780i_irq); - return bRC; - } - } - - if (mwave_uart_io) { - for (i = 0; i < ARRAY_SIZE(ausUartBases); i++) { - if (mwave_uart_io == ausUartBases[i]) - break; - } - if (i == ARRAY_SIZE(ausUartBases)) { - PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_set_DSP_cfg: Error: Invalid mwave_uart_io address %x. Aborting.\n", mwave_uart_io); - return bRC; - } - uartio_index = i; - } - - - if (mwave_uart_irq) { - for (i = 0; i < ARRAY_SIZE(ausUartIrqs); i++) { - if (mwave_uart_irq == ausUartIrqs[i]) - break; - } - if (i == ARRAY_SIZE(ausUartIrqs)) { - PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_set_DSP_cfg: Error: Invalid mwave_uart_irq %x. Aborting.\n", mwave_uart_irq); - return bRC; - } - } - - if (mwave_uart_irq || mwave_uart_io) { - - /* Check serial port A */ - bRC = smapi_request(0x1402, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - /* bRC == 0 */ - if (usBX & 0x0100) { /* serial port A is present */ - if (usCX & 1) { /* serial port is enabled */ - if ((usSI & 0xFF) == mwave_uart_irq) { -#ifndef MWAVE_FUTZ_WITH_OTHER_DEVICES - PRINTK_ERROR(KERN_ERR_MWAVE - "smapi::smapi_set_DSP_cfg: Serial port A irq %x conflicts with mwave_uart_irq %x\n", usSI & 0xFF, mwave_uart_irq); -#else - PRINTK_3(TRACE_SMAPI, - "smapi::smapi_set_DSP_cfg: Serial port A irq %x conflicts with mwave_uart_irq %x\n", usSI & 0xFF, mwave_uart_irq); -#endif -#ifdef MWAVE_FUTZ_WITH_OTHER_DEVICES - PRINTK_1(TRACE_SMAPI, - "smapi::smapi_set_DSP_cfg Disabling conflicting serial port\n"); - bRC = smapi_request(0x1403, 0x0100, 0, usSI, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - bRC = smapi_request(0x1402, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; -#else - goto exit_conflict; -#endif - } else { - if ((usSI >> 8) == uartio_index) { -#ifndef MWAVE_FUTZ_WITH_OTHER_DEVICES - PRINTK_ERROR(KERN_ERR_MWAVE - "smapi::smapi_set_DSP_cfg: Serial port A base I/O address %x conflicts with mwave uart I/O %x\n", ausUartBases[usSI >> 8], ausUartBases[uartio_index]); -#else - PRINTK_3(TRACE_SMAPI, - "smapi::smapi_set_DSP_cfg: Serial port A base I/O address %x conflicts with mwave uart I/O %x\n", ausUartBases[usSI >> 8], ausUartBases[uartio_index]); -#endif -#ifdef MWAVE_FUTZ_WITH_OTHER_DEVICES - PRINTK_1(TRACE_SMAPI, - "smapi::smapi_set_DSP_cfg Disabling conflicting serial port A\n"); - bRC = smapi_request (0x1403, 0x0100, 0, usSI, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - bRC = smapi_request (0x1402, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; -#else - goto exit_conflict; -#endif - } - } - } - } - - /* Check serial port B */ - bRC = smapi_request(0x1404, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - /* bRC == 0 */ - if (usBX & 0x0100) { /* serial port B is present */ - if (usCX & 1) { /* serial port is enabled */ - if ((usSI & 0xFF) == mwave_uart_irq) { -#ifndef MWAVE_FUTZ_WITH_OTHER_DEVICES - PRINTK_ERROR(KERN_ERR_MWAVE - "smapi::smapi_set_DSP_cfg: Serial port B irq %x conflicts with mwave_uart_irq %x\n", usSI & 0xFF, mwave_uart_irq); -#else - PRINTK_3(TRACE_SMAPI, - "smapi::smapi_set_DSP_cfg: Serial port B irq %x conflicts with mwave_uart_irq %x\n", usSI & 0xFF, mwave_uart_irq); -#endif -#ifdef MWAVE_FUTZ_WITH_OTHER_DEVICES - PRINTK_1(TRACE_SMAPI, - "smapi::smapi_set_DSP_cfg Disabling conflicting serial port B\n"); - bRC = smapi_request(0x1405, 0x0100, 0, usSI, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - bRC = smapi_request(0x1404, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; -#else - goto exit_conflict; -#endif - } else { - if ((usSI >> 8) == uartio_index) { -#ifndef MWAVE_FUTZ_WITH_OTHER_DEVICES - PRINTK_ERROR(KERN_ERR_MWAVE - "smapi::smapi_set_DSP_cfg: Serial port B base I/O address %x conflicts with mwave uart I/O %x\n", ausUartBases[usSI >> 8], ausUartBases[uartio_index]); -#else - PRINTK_3(TRACE_SMAPI, - "smapi::smapi_set_DSP_cfg: Serial port B base I/O address %x conflicts with mwave uart I/O %x\n", ausUartBases[usSI >> 8], ausUartBases[uartio_index]); -#endif -#ifdef MWAVE_FUTZ_WITH_OTHER_DEVICES - PRINTK_1 (TRACE_SMAPI, - "smapi::smapi_set_DSP_cfg Disabling conflicting serial port B\n"); - bRC = smapi_request (0x1405, 0x0100, 0, usSI, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - bRC = smapi_request (0x1404, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; -#else - goto exit_conflict; -#endif - } - } - } - } - - /* Check IR port */ - bRC = smapi_request(0x1700, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - bRC = smapi_request(0x1704, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - /* bRC == 0 */ - if ((usCX & 0xff) != 0xff) { /* IR port not disabled */ - if ((usCX & 0xff) == mwave_uart_irq) { -#ifndef MWAVE_FUTZ_WITH_OTHER_DEVICES - PRINTK_ERROR(KERN_ERR_MWAVE - "smapi::smapi_set_DSP_cfg: IR port irq %x conflicts with mwave_uart_irq %x\n", usCX & 0xff, mwave_uart_irq); -#else - PRINTK_3(TRACE_SMAPI, - "smapi::smapi_set_DSP_cfg: IR port irq %x conflicts with mwave_uart_irq %x\n", usCX & 0xff, mwave_uart_irq); -#endif -#ifdef MWAVE_FUTZ_WITH_OTHER_DEVICES - PRINTK_1(TRACE_SMAPI, - "smapi::smapi_set_DSP_cfg Disabling conflicting IR port\n"); - bRC = smapi_request(0x1701, 0x0100, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - bRC = smapi_request(0x1700, 0, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - bRC = smapi_request(0x1705, 0x01ff, 0, usSI, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - bRC = smapi_request(0x1704, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; -#else - goto exit_conflict; -#endif - } else { - if ((usSI & 0xff) == uartio_index) { -#ifndef MWAVE_FUTZ_WITH_OTHER_DEVICES - PRINTK_ERROR(KERN_ERR_MWAVE - "smapi::smapi_set_DSP_cfg: IR port base I/O address %x conflicts with mwave uart I/O %x\n", ausUartBases[usSI & 0xff], ausUartBases[uartio_index]); -#else - PRINTK_3(TRACE_SMAPI, - "smapi::smapi_set_DSP_cfg: IR port base I/O address %x conflicts with mwave uart I/O %x\n", ausUartBases[usSI & 0xff], ausUartBases[uartio_index]); -#endif -#ifdef MWAVE_FUTZ_WITH_OTHER_DEVICES - PRINTK_1(TRACE_SMAPI, - "smapi::smapi_set_DSP_cfg Disabling conflicting IR port\n"); - bRC = smapi_request(0x1701, 0x0100, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - bRC = smapi_request(0x1700, 0, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - bRC = smapi_request(0x1705, 0x01ff, 0, usSI, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - bRC = smapi_request(0x1704, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; -#else - goto exit_conflict; -#endif - } - } - } - } - - bRC = smapi_request(0x1802, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - - if (mwave_3780i_io) { - usDI = dspio_index; - } - if (mwave_3780i_irq) { - usSI = (usSI & 0xff00) | mwave_3780i_irq; - } - - bRC = smapi_request(0x1803, 0x0101, usDI, usSI, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - - bRC = smapi_request(0x1804, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - - if (mwave_uart_io) { - usSI = (usSI & 0x00ff) | (uartio_index << 8); - } - if (mwave_uart_irq) { - usSI = (usSI & 0xff00) | mwave_uart_irq; - } - bRC = smapi_request(0x1805, 0x0101, 0, usSI, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - - bRC = smapi_request(0x1802, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - - bRC = smapi_request(0x1804, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - -/* normal exit: */ - PRINTK_1(TRACE_SMAPI, "smapi::smapi_set_DSP_cfg exit\n"); - return 0; - -exit_conflict: - /* Message has already been printed */ - return -EIO; - -exit_smapi_request_error: - PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_set_DSP_cfg exit on smapi_request error bRC %x\n", bRC); - return bRC; -} - - -int smapi_set_DSP_power_state(bool bOn) -{ - int bRC; - unsigned short usAX, usBX, usCX, usDX, usDI, usSI; - unsigned short usPowerFunction; - - PRINTK_2(TRACE_SMAPI, "smapi::smapi_set_DSP_power_state entry bOn %x\n", bOn); - - usPowerFunction = (bOn) ? 1 : 0; - - bRC = smapi_request(0x4901, 0x0000, 0, usPowerFunction, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - - PRINTK_2(TRACE_SMAPI, "smapi::smapi_set_DSP_power_state exit bRC %x\n", bRC); - - return bRC; -} - -#if 0 -static int SmapiQuerySystemID(void) -{ - int bRC = -EIO; - unsigned short usAX = 0xffff, usBX = 0xffff, usCX = 0xffff, - usDX = 0xffff, usDI = 0xffff, usSI = 0xffff; - - printk("smapi::SmapiQUerySystemID entry\n"); - bRC = smapi_request(0x0000, 0, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - - if (bRC == 0) { - printk("AX=%x, BX=%x, CX=%x, DX=%x, DI=%x, SI=%x\n", - usAX, usBX, usCX, usDX, usDI, usSI); - } else { - printk("smapi::SmapiQuerySystemID smapi_request error\n"); - } - - return bRC; -} -#endif /* 0 */ - -int smapi_init(void) -{ - int retval = -EIO; - unsigned short usSmapiID = 0; - unsigned long flags; - - PRINTK_1(TRACE_SMAPI, "smapi::smapi_init entry\n"); - - spin_lock_irqsave(&rtc_lock, flags); - usSmapiID = CMOS_READ(0x7C); - usSmapiID |= (CMOS_READ(0x7D) << 8); - spin_unlock_irqrestore(&rtc_lock, flags); - PRINTK_2(TRACE_SMAPI, "smapi::smapi_init usSmapiID %x\n", usSmapiID); - - if (usSmapiID == 0x5349) { - spin_lock_irqsave(&rtc_lock, flags); - g_usSmapiPort = CMOS_READ(0x7E); - g_usSmapiPort |= (CMOS_READ(0x7F) << 8); - spin_unlock_irqrestore(&rtc_lock, flags); - if (g_usSmapiPort == 0) { - PRINTK_ERROR("smapi::smapi_init, ERROR unable to read from SMAPI port\n"); - } else { - PRINTK_2(TRACE_SMAPI, - "smapi::smapi_init, exit true g_usSmapiPort %x\n", - g_usSmapiPort); - retval = 0; - //SmapiQuerySystemID(); - } - } else { - PRINTK_ERROR("smapi::smapi_init, ERROR invalid usSmapiID\n"); - retval = -ENXIO; - } - - return retval; -} diff --git a/drivers/char/mwave/smapi.h b/drivers/char/mwave/smapi.h deleted file mode 100644 index ebc206b000b9..000000000000 --- a/drivers/char/mwave/smapi.h +++ /dev/null @@ -1,76 +0,0 @@ -/* -* -* smapi.h -- declarations for SMAPI interface routines -* -* -* Written By: Mike Sullivan IBM Corporation -* -* Copyright (C) 1999 IBM Corporation -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* NO WARRANTY -* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR -* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT -* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, -* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is -* solely responsible for determining the appropriateness of using and -* distributing the Program and assumes all risks associated with its -* exercise of rights under this Agreement, including but not limited to -* the risks and costs of program errors, damage to or loss of data, -* programs or equipment, and unavailability or interruption of operations. -* -* DISCLAIMER OF LIABILITY -* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* -* 10/23/2000 - Alpha Release -* First release to the public -*/ - -#ifndef _LINUX_SMAPI_H -#define _LINUX_SMAPI_H - -typedef struct { - int bDSPPresent; - int bDSPEnabled; - int bModemEnabled; - int bMIDIEnabled; - int bSblstEnabled; - unsigned short usDspIRQ; - unsigned short usDspDMA; - unsigned short usDspBaseIO; - unsigned short usUartIRQ; - unsigned short usUartBaseIO; - unsigned short usMidiIRQ; - unsigned short usMidiBaseIO; - unsigned short usSndblstIRQ; - unsigned short usSndblstDMA; - unsigned short usSndblstBaseIO; -} SMAPI_DSP_SETTINGS; - -int smapi_init(void); -int smapi_query_DSP_cfg(SMAPI_DSP_SETTINGS * pSettings); -int smapi_set_DSP_cfg(void); -int smapi_set_DSP_power_state(bool bOn); - - -#endif diff --git a/drivers/char/mwave/tp3780i.c b/drivers/char/mwave/tp3780i.c deleted file mode 100644 index 83eaffeb22c8..000000000000 --- a/drivers/char/mwave/tp3780i.c +++ /dev/null @@ -1,574 +0,0 @@ -/* -* -* tp3780i.c -- board driver for 3780i on ThinkPads -* -* -* Written By: Mike Sullivan IBM Corporation -* -* Copyright (C) 1999 IBM Corporation -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* NO WARRANTY -* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR -* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT -* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, -* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is -* solely responsible for determining the appropriateness of using and -* distributing the Program and assumes all risks associated with its -* exercise of rights under this Agreement, including but not limited to -* the risks and costs of program errors, damage to or loss of data, -* programs or equipment, and unavailability or interruption of operations. -* -* DISCLAIMER OF LIABILITY -* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* -* 10/23/2000 - Alpha Release -* First release to the public -*/ - -#include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/ptrace.h> -#include <linux/ioport.h> -#include <asm/io.h> -#include "smapi.h" -#include "mwavedd.h" -#include "tp3780i.h" -#include "3780i.h" -#include "mwavepub.h" - -static unsigned short s_ausThinkpadIrqToField[16] = - { 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, 0x0002, 0x0003, 0xFFFF, 0x0004, - 0xFFFF, 0xFFFF, 0x0005, 0x0006, 0xFFFF, 0xFFFF, 0xFFFF, 0x0007 }; -static unsigned short s_ausThinkpadDmaToField[8] = - { 0x0001, 0x0002, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0003, 0x0004 }; -static unsigned short s_numIrqs = 16, s_numDmas = 8; - - -static void EnableSRAM(THINKPAD_BD_DATA * pBDData) -{ - DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings; - unsigned short usDspBaseIO = pSettings->usDspBaseIO; - DSP_GPIO_OUTPUT_DATA_15_8 rGpioOutputData; - DSP_GPIO_DRIVER_ENABLE_15_8 rGpioDriverEnable; - DSP_GPIO_MODE_15_8 rGpioMode; - - PRINTK_1(TRACE_TP3780I, "tp3780i::EnableSRAM, entry\n"); - - MKWORD(rGpioMode) = ReadMsaCfg(DSP_GpioModeControl_15_8); - rGpioMode.GpioMode10 = 0; - WriteMsaCfg(DSP_GpioModeControl_15_8, MKWORD(rGpioMode)); - - MKWORD(rGpioDriverEnable) = 0; - rGpioDriverEnable.Enable10 = true; - rGpioDriverEnable.Mask10 = true; - WriteMsaCfg(DSP_GpioDriverEnable_15_8, MKWORD(rGpioDriverEnable)); - - MKWORD(rGpioOutputData) = 0; - rGpioOutputData.Latch10 = 0; - rGpioOutputData.Mask10 = true; - WriteMsaCfg(DSP_GpioOutputData_15_8, MKWORD(rGpioOutputData)); - - PRINTK_1(TRACE_TP3780I, "tp3780i::EnableSRAM exit\n"); -} - - -static irqreturn_t UartInterrupt(int irq, void *dev_id) -{ - PRINTK_3(TRACE_TP3780I, - "tp3780i::UartInterrupt entry irq %x dev_id %p\n", irq, dev_id); - return IRQ_HANDLED; -} - -static irqreturn_t DspInterrupt(int irq, void *dev_id) -{ - pMWAVE_DEVICE_DATA pDrvData = &mwave_s_mdd; - DSP_3780I_CONFIG_SETTINGS *pSettings = &pDrvData->rBDData.rDspSettings; - unsigned short usDspBaseIO = pSettings->usDspBaseIO; - unsigned short usIPCSource = 0, usIsolationMask, usPCNum; - - PRINTK_3(TRACE_TP3780I, - "tp3780i::DspInterrupt entry irq %x dev_id %p\n", irq, dev_id); - - if (dsp3780I_GetIPCSource(usDspBaseIO, &usIPCSource) == 0) { - PRINTK_2(TRACE_TP3780I, - "tp3780i::DspInterrupt, return from dsp3780i_GetIPCSource, usIPCSource %x\n", - usIPCSource); - usIsolationMask = 1; - for (usPCNum = 1; usPCNum <= 16; usPCNum++) { - if (usIPCSource & usIsolationMask) { - usIPCSource &= ~usIsolationMask; - PRINTK_3(TRACE_TP3780I, - "tp3780i::DspInterrupt usPCNum %x usIPCSource %x\n", - usPCNum, usIPCSource); - if (pDrvData->IPCs[usPCNum - 1].usIntCount == 0) { - pDrvData->IPCs[usPCNum - 1].usIntCount = 1; - } - PRINTK_2(TRACE_TP3780I, - "tp3780i::DspInterrupt usIntCount %x\n", - pDrvData->IPCs[usPCNum - 1].usIntCount); - if (pDrvData->IPCs[usPCNum - 1].bIsEnabled == true) { - PRINTK_2(TRACE_TP3780I, - "tp3780i::DspInterrupt, waking up usPCNum %x\n", - usPCNum - 1); - wake_up_interruptible(&pDrvData->IPCs[usPCNum - 1].ipc_wait_queue); - } else { - PRINTK_2(TRACE_TP3780I, - "tp3780i::DspInterrupt, no one waiting for IPC %x\n", - usPCNum - 1); - } - } - if (usIPCSource == 0) - break; - /* try next IPC */ - usIsolationMask = usIsolationMask << 1; - } - } else { - PRINTK_1(TRACE_TP3780I, - "tp3780i::DspInterrupt, return false from dsp3780i_GetIPCSource\n"); - } - PRINTK_1(TRACE_TP3780I, "tp3780i::DspInterrupt exit\n"); - return IRQ_HANDLED; -} - - -int tp3780I_InitializeBoardData(THINKPAD_BD_DATA * pBDData) -{ - int retval = 0; - DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings; - - - PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_InitializeBoardData entry pBDData %p\n", pBDData); - - pBDData->bDSPEnabled = false; - pSettings->bInterruptClaimed = false; - - retval = smapi_init(); - if (retval) { - PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_InitializeBoardData: Error: SMAPI is not available on this machine\n"); - } else { - if (mwave_3780i_irq || mwave_3780i_io || mwave_uart_irq || mwave_uart_io) { - retval = smapi_set_DSP_cfg(); - } - } - - PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_InitializeBoardData exit retval %x\n", retval); - - return retval; -} - -void tp3780I_Cleanup(THINKPAD_BD_DATA *pBDData) -{ - PRINTK_2(TRACE_TP3780I, - "tp3780i::tp3780I_Cleanup entry and exit pBDData %p\n", pBDData); -} - -int tp3780I_CalcResources(THINKPAD_BD_DATA * pBDData) -{ - SMAPI_DSP_SETTINGS rSmapiInfo; - DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings; - - PRINTK_2(TRACE_TP3780I, - "tp3780i::tp3780I_CalcResources entry pBDData %p\n", pBDData); - - if (smapi_query_DSP_cfg(&rSmapiInfo)) { - PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_CalcResources: Error: Could not query DSP config. Aborting.\n"); - return -EIO; - } - - /* Sanity check */ - if ( - ( rSmapiInfo.usDspIRQ == 0 ) - || ( rSmapiInfo.usDspBaseIO == 0 ) - || ( rSmapiInfo.usUartIRQ == 0 ) - || ( rSmapiInfo.usUartBaseIO == 0 ) - ) { - PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_CalcResources: Error: Illegal resource setting. Aborting.\n"); - return -EIO; - } - - pSettings->bDSPEnabled = (rSmapiInfo.bDSPEnabled && rSmapiInfo.bDSPPresent); - pSettings->bModemEnabled = rSmapiInfo.bModemEnabled; - pSettings->usDspIrq = rSmapiInfo.usDspIRQ; - pSettings->usDspDma = rSmapiInfo.usDspDMA; - pSettings->usDspBaseIO = rSmapiInfo.usDspBaseIO; - pSettings->usUartIrq = rSmapiInfo.usUartIRQ; - pSettings->usUartBaseIO = rSmapiInfo.usUartBaseIO; - - pSettings->uDStoreSize = TP_ABILITIES_DATA_SIZE; - pSettings->uIStoreSize = TP_ABILITIES_INST_SIZE; - pSettings->uIps = TP_ABILITIES_INTS_PER_SEC; - - if (pSettings->bDSPEnabled && pSettings->bModemEnabled && pSettings->usDspIrq == pSettings->usUartIrq) { - pBDData->bShareDspIrq = pBDData->bShareUartIrq = 1; - } else { - pBDData->bShareDspIrq = pBDData->bShareUartIrq = 0; - } - - PRINTK_1(TRACE_TP3780I, "tp3780i::tp3780I_CalcResources exit\n"); - - return 0; -} - - -int tp3780I_ClaimResources(THINKPAD_BD_DATA * pBDData) -{ - int retval = 0; - DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings; - struct resource *pres; - - PRINTK_2(TRACE_TP3780I, - "tp3780i::tp3780I_ClaimResources entry pBDData %p\n", pBDData); - - pres = request_region(pSettings->usDspBaseIO, 16, "mwave_3780i"); - if ( pres == NULL ) retval = -EIO; - - if (retval) { - PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_ClaimResources: Error: Could not claim I/O region starting at %x\n", pSettings->usDspBaseIO); - retval = -EIO; - } - - PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_ClaimResources exit retval %x\n", retval); - - return retval; -} - -int tp3780I_ReleaseResources(THINKPAD_BD_DATA * pBDData) -{ - int retval = 0; - DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings; - - PRINTK_2(TRACE_TP3780I, - "tp3780i::tp3780I_ReleaseResources entry pBDData %p\n", pBDData); - - release_region(pSettings->usDspBaseIO & (~3), 16); - - if (pSettings->bInterruptClaimed) { - free_irq(pSettings->usDspIrq, NULL); - pSettings->bInterruptClaimed = false; - } - - PRINTK_2(TRACE_TP3780I, - "tp3780i::tp3780I_ReleaseResources exit retval %x\n", retval); - - return retval; -} - - - -int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData) -{ - DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings; - bool bDSPPoweredUp = false, bInterruptAllocated = false; - - PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_EnableDSP entry pBDData %p\n", pBDData); - - if (pBDData->bDSPEnabled) { - PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_EnableDSP: Error: DSP already enabled!\n"); - goto exit_cleanup; - } - - if (!pSettings->bDSPEnabled) { - PRINTK_ERROR(KERN_ERR_MWAVE "tp3780::tp3780I_EnableDSP: Error: pSettings->bDSPEnabled not set\n"); - goto exit_cleanup; - } - - if ( - (pSettings->usDspIrq >= s_numIrqs) - || (pSettings->usDspDma >= s_numDmas) - || (s_ausThinkpadIrqToField[pSettings->usDspIrq] == 0xFFFF) - || (s_ausThinkpadDmaToField[pSettings->usDspDma] == 0xFFFF) - ) { - PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_EnableDSP: Error: invalid irq %x\n", pSettings->usDspIrq); - goto exit_cleanup; - } - - if ( - ((pSettings->usDspBaseIO & 0xF00F) != 0) - || (pSettings->usDspBaseIO & 0x0FF0) == 0 - ) { - PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_EnableDSP: Error: Invalid DSP base I/O address %x\n", pSettings->usDspBaseIO); - goto exit_cleanup; - } - - if (pSettings->bModemEnabled) { - if ( - pSettings->usUartIrq >= s_numIrqs - || s_ausThinkpadIrqToField[pSettings->usUartIrq] == 0xFFFF - ) { - PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_EnableDSP: Error: Invalid UART IRQ %x\n", pSettings->usUartIrq); - goto exit_cleanup; - } - switch (pSettings->usUartBaseIO) { - case 0x03F8: - case 0x02F8: - case 0x03E8: - case 0x02E8: - break; - - default: - PRINTK_ERROR("tp3780i::tp3780I_EnableDSP: Error: Invalid UART base I/O address %x\n", pSettings->usUartBaseIO); - goto exit_cleanup; - } - } - - pSettings->bDspIrqActiveLow = pSettings->bDspIrqPulse = true; - pSettings->bUartIrqActiveLow = pSettings->bUartIrqPulse = true; - - if (pBDData->bShareDspIrq) { - pSettings->bDspIrqActiveLow = false; - } - if (pBDData->bShareUartIrq) { - pSettings->bUartIrqActiveLow = false; - } - - pSettings->usNumTransfers = TP_CFG_NumTransfers; - pSettings->usReRequest = TP_CFG_RerequestTimer; - pSettings->bEnableMEMCS16 = TP_CFG_MEMCS16; - pSettings->usIsaMemCmdWidth = TP_CFG_IsaMemCmdWidth; - pSettings->bGateIOCHRDY = TP_CFG_GateIOCHRDY; - pSettings->bEnablePwrMgmt = TP_CFG_EnablePwrMgmt; - pSettings->usHBusTimerLoadValue = TP_CFG_HBusTimerValue; - pSettings->bDisableLBusTimeout = TP_CFG_DisableLBusTimeout; - pSettings->usN_Divisor = TP_CFG_N_Divisor; - pSettings->usM_Multiplier = TP_CFG_M_Multiplier; - pSettings->bPllBypass = TP_CFG_PllBypass; - pSettings->usChipletEnable = TP_CFG_ChipletEnable; - - if (request_irq(pSettings->usUartIrq, &UartInterrupt, 0, "mwave_uart", NULL)) { - PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_EnableDSP: Error: Could not get UART IRQ %x\n", pSettings->usUartIrq); - goto exit_cleanup; - } else { /* no conflict just release */ - free_irq(pSettings->usUartIrq, NULL); - } - - if (request_irq(pSettings->usDspIrq, &DspInterrupt, 0, "mwave_3780i", NULL)) { - PRINTK_ERROR("tp3780i::tp3780I_EnableDSP: Error: Could not get 3780i IRQ %x\n", pSettings->usDspIrq); - goto exit_cleanup; - } else { - PRINTK_3(TRACE_TP3780I, - "tp3780i::tp3780I_EnableDSP, got interrupt %x bShareDspIrq %x\n", - pSettings->usDspIrq, pBDData->bShareDspIrq); - bInterruptAllocated = true; - pSettings->bInterruptClaimed = true; - } - - smapi_set_DSP_power_state(false); - if (smapi_set_DSP_power_state(true)) { - PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_EnableDSP: Error: smapi_set_DSP_power_state(true) failed\n"); - goto exit_cleanup; - } else { - bDSPPoweredUp = true; - } - - if (dsp3780I_EnableDSP(pSettings, s_ausThinkpadIrqToField, s_ausThinkpadDmaToField)) { - PRINTK_ERROR("tp3780i::tp3780I_EnableDSP: Error: dsp7880I_EnableDSP() failed\n"); - goto exit_cleanup; - } - - EnableSRAM(pBDData); - - pBDData->bDSPEnabled = true; - - PRINTK_1(TRACE_TP3780I, "tp3780i::tp3780I_EnableDSP exit\n"); - - return 0; - -exit_cleanup: - PRINTK_ERROR("tp3780i::tp3780I_EnableDSP: Cleaning up\n"); - if (bDSPPoweredUp) - smapi_set_DSP_power_state(false); - if (bInterruptAllocated) { - free_irq(pSettings->usDspIrq, NULL); - pSettings->bInterruptClaimed = false; - } - return -EIO; -} - - -int tp3780I_DisableDSP(THINKPAD_BD_DATA * pBDData) -{ - int retval = 0; - DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings; - - PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_DisableDSP entry pBDData %p\n", pBDData); - - if (pBDData->bDSPEnabled) { - dsp3780I_DisableDSP(&pBDData->rDspSettings); - if (pSettings->bInterruptClaimed) { - free_irq(pSettings->usDspIrq, NULL); - pSettings->bInterruptClaimed = false; - } - smapi_set_DSP_power_state(false); - pBDData->bDSPEnabled = false; - } - - PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_DisableDSP exit retval %x\n", retval); - - return retval; -} - - -int tp3780I_ResetDSP(THINKPAD_BD_DATA * pBDData) -{ - int retval = 0; - DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings; - - PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_ResetDSP entry pBDData %p\n", - pBDData); - - if (dsp3780I_Reset(pSettings) == 0) { - EnableSRAM(pBDData); - } else { - retval = -EIO; - } - - PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_ResetDSP exit retval %x\n", retval); - - return retval; -} - - -int tp3780I_StartDSP(THINKPAD_BD_DATA * pBDData) -{ - int retval = 0; - DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings; - - PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_StartDSP entry pBDData %p\n", pBDData); - - if (dsp3780I_Run(pSettings) == 0) { - // @BUG @TBD EnableSRAM(pBDData); - } else { - retval = -EIO; - } - - PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_StartDSP exit retval %x\n", retval); - - return retval; -} - - -int tp3780I_QueryAbilities(THINKPAD_BD_DATA * pBDData, MW_ABILITIES * pAbilities) -{ - PRINTK_2(TRACE_TP3780I, - "tp3780i::tp3780I_QueryAbilities entry pBDData %p\n", pBDData); - - memset(pAbilities, 0, sizeof(*pAbilities)); - /* fill out standard constant fields */ - pAbilities->instr_per_sec = pBDData->rDspSettings.uIps; - pAbilities->data_size = pBDData->rDspSettings.uDStoreSize; - pAbilities->inst_size = pBDData->rDspSettings.uIStoreSize; - pAbilities->bus_dma_bw = pBDData->rDspSettings.uDmaBandwidth; - - /* fill out dynamically determined fields */ - pAbilities->component_list[0] = 0x00010000 | MW_ADC_MASK; - pAbilities->component_list[1] = 0x00010000 | MW_ACI_MASK; - pAbilities->component_list[2] = 0x00010000 | MW_AIC1_MASK; - pAbilities->component_list[3] = 0x00010000 | MW_AIC2_MASK; - pAbilities->component_list[4] = 0x00010000 | MW_CDDAC_MASK; - pAbilities->component_list[5] = 0x00010000 | MW_MIDI_MASK; - pAbilities->component_list[6] = 0x00010000 | MW_UART_MASK; - pAbilities->component_count = 7; - - /* Fill out Mwave OS and BIOS task names */ - - memcpy(pAbilities->mwave_os_name, TP_ABILITIES_MWAVEOS_NAME, - sizeof(TP_ABILITIES_MWAVEOS_NAME)); - memcpy(pAbilities->bios_task_name, TP_ABILITIES_BIOSTASK_NAME, - sizeof(TP_ABILITIES_BIOSTASK_NAME)); - - PRINTK_1(TRACE_TP3780I, - "tp3780i::tp3780I_QueryAbilities exit retval=SUCCESSFUL\n"); - - return 0; -} - -int tp3780I_ReadWriteDspDStore(THINKPAD_BD_DATA * pBDData, unsigned int uOpcode, - void __user *pvBuffer, unsigned int uCount, - unsigned long ulDSPAddr) -{ - int retval = 0; - DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings; - unsigned short usDspBaseIO = pSettings->usDspBaseIO; - bool bRC = 0; - - PRINTK_6(TRACE_TP3780I, - "tp3780i::tp3780I_ReadWriteDspDStore entry pBDData %p, uOpcode %x, pvBuffer %p, uCount %x, ulDSPAddr %lx\n", - pBDData, uOpcode, pvBuffer, uCount, ulDSPAddr); - - if (pBDData->bDSPEnabled) { - switch (uOpcode) { - case IOCTL_MW_READ_DATA: - bRC = dsp3780I_ReadDStore(usDspBaseIO, pvBuffer, uCount, ulDSPAddr); - break; - - case IOCTL_MW_READCLEAR_DATA: - bRC = dsp3780I_ReadAndClearDStore(usDspBaseIO, pvBuffer, uCount, ulDSPAddr); - break; - - case IOCTL_MW_WRITE_DATA: - bRC = dsp3780I_WriteDStore(usDspBaseIO, pvBuffer, uCount, ulDSPAddr); - break; - } - } - - retval = (bRC) ? -EIO : 0; - PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_ReadWriteDspDStore exit retval %x\n", retval); - - return retval; -} - - -int tp3780I_ReadWriteDspIStore(THINKPAD_BD_DATA * pBDData, unsigned int uOpcode, - void __user *pvBuffer, unsigned int uCount, - unsigned long ulDSPAddr) -{ - int retval = 0; - DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings; - unsigned short usDspBaseIO = pSettings->usDspBaseIO; - bool bRC = 0; - - PRINTK_6(TRACE_TP3780I, - "tp3780i::tp3780I_ReadWriteDspIStore entry pBDData %p, uOpcode %x, pvBuffer %p, uCount %x, ulDSPAddr %lx\n", - pBDData, uOpcode, pvBuffer, uCount, ulDSPAddr); - - if (pBDData->bDSPEnabled) { - switch (uOpcode) { - case IOCTL_MW_READ_INST: - bRC = dsp3780I_ReadIStore(usDspBaseIO, pvBuffer, uCount, ulDSPAddr); - break; - - case IOCTL_MW_WRITE_INST: - bRC = dsp3780I_WriteIStore(usDspBaseIO, pvBuffer, uCount, ulDSPAddr); - break; - } - } - - retval = (bRC) ? -EIO : 0; - - PRINTK_2(TRACE_TP3780I, - "tp3780i::tp3780I_ReadWriteDspIStore exit retval %x\n", retval); - - return retval; -} - diff --git a/drivers/char/mwave/tp3780i.h b/drivers/char/mwave/tp3780i.h deleted file mode 100644 index 8bd976d42fae..000000000000 --- a/drivers/char/mwave/tp3780i.h +++ /dev/null @@ -1,103 +0,0 @@ -/* -* -* tp3780i.h -- declarations for tp3780i.c -* -* -* Written By: Mike Sullivan IBM Corporation -* -* Copyright (C) 1999 IBM Corporation -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* NO WARRANTY -* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR -* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT -* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, -* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is -* solely responsible for determining the appropriateness of using and -* distributing the Program and assumes all risks associated with its -* exercise of rights under this Agreement, including but not limited to -* the risks and costs of program errors, damage to or loss of data, -* programs or equipment, and unavailability or interruption of operations. -* -* DISCLAIMER OF LIABILITY -* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* -* 10/23/2000 - Alpha Release -* First release to the public -*/ - -#ifndef _LINUX_TP3780I_H -#define _LINUX_TP3780I_H - -#include <asm/io.h> -#include "mwavepub.h" - - -/* DSP abilities constants for 3780i based Thinkpads */ -#define TP_ABILITIES_INTS_PER_SEC 39160800 -#define TP_ABILITIES_DATA_SIZE 32768 -#define TP_ABILITIES_INST_SIZE 32768 -#define TP_ABILITIES_MWAVEOS_NAME "mwaveos0700.dsp" -#define TP_ABILITIES_BIOSTASK_NAME "mwbio701.dsp" - - -/* DSP configuration values for 3780i based Thinkpads */ -#define TP_CFG_NumTransfers 3 /* 16 transfers */ -#define TP_CFG_RerequestTimer 1 /* 2 usec */ -#define TP_CFG_MEMCS16 0 /* Disabled, 16-bit memory assumed */ -#define TP_CFG_IsaMemCmdWidth 3 /* 295 nsec (16-bit) */ -#define TP_CFG_GateIOCHRDY 0 /* No IOCHRDY gating */ -#define TP_CFG_EnablePwrMgmt 1 /* Enable low poser suspend/resume */ -#define TP_CFG_HBusTimerValue 255 /* HBus timer load value */ -#define TP_CFG_DisableLBusTimeout 0 /* Enable LBus timeout */ -#define TP_CFG_N_Divisor 32 /* Clock = 39.1608 Mhz */ -#define TP_CFG_M_Multiplier 37 /* " */ -#define TP_CFG_PllBypass 0 /* don't bypass */ -#define TP_CFG_ChipletEnable 0xFFFF /* Enable all chiplets */ - -typedef struct { - int bDSPEnabled; - int bShareDspIrq; - int bShareUartIrq; - DSP_3780I_CONFIG_SETTINGS rDspSettings; -} THINKPAD_BD_DATA; - -int tp3780I_InitializeBoardData(THINKPAD_BD_DATA * pBDData); -int tp3780I_CalcResources(THINKPAD_BD_DATA * pBDData); -int tp3780I_ClaimResources(THINKPAD_BD_DATA * pBDData); -int tp3780I_ReleaseResources(THINKPAD_BD_DATA * pBDData); -int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData); -int tp3780I_DisableDSP(THINKPAD_BD_DATA * pBDData); -int tp3780I_ResetDSP(THINKPAD_BD_DATA * pBDData); -int tp3780I_StartDSP(THINKPAD_BD_DATA * pBDData); -int tp3780I_QueryAbilities(THINKPAD_BD_DATA * pBDData, MW_ABILITIES * pAbilities); -void tp3780I_Cleanup(THINKPAD_BD_DATA *pBDData); -int tp3780I_ReadWriteDspDStore(THINKPAD_BD_DATA * pBDData, unsigned int uOpcode, - void __user *pvBuffer, unsigned int uCount, - unsigned long ulDSPAddr); -int tp3780I_ReadWriteDspIStore(THINKPAD_BD_DATA * pBDData, unsigned int uOpcode, - void __user *pvBuffer, unsigned int uCount, - unsigned long ulDSPAddr); - - -#endif diff --git a/drivers/char/nsc_gpio.c b/drivers/char/nsc_gpio.c index da930c72bc74..2c9b12b85435 100644 --- a/drivers/char/nsc_gpio.c +++ b/drivers/char/nsc_gpio.c @@ -121,20 +121,6 @@ EXPORT_SYMBOL(nsc_gpio_write); EXPORT_SYMBOL(nsc_gpio_read); EXPORT_SYMBOL(nsc_gpio_dump); -static int __init nsc_gpio_init(void) -{ - printk(KERN_DEBUG NAME " initializing\n"); - return 0; -} - -static void __exit nsc_gpio_cleanup(void) -{ - printk(KERN_DEBUG NAME " cleanup\n"); -} - -module_init(nsc_gpio_init); -module_exit(nsc_gpio_cleanup); - MODULE_AUTHOR("Jim Cromie <jim.cromie@gmail.com>"); MODULE_DESCRIPTION("NatSemi GPIO Common Methods"); MODULE_LICENSE("GPL"); diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c index 9eff426a9286..e89cc1f1c89e 100644 --- a/drivers/char/nvram.c +++ b/drivers/char/nvram.c @@ -53,7 +53,6 @@ #include <asm/nvram.h> #endif -static DEFINE_MUTEX(nvram_mutex); static DEFINE_SPINLOCK(nvram_state_lock); static int nvram_open_cnt; /* #times opened */ static int nvram_open_mode; /* special open modes */ @@ -310,11 +309,8 @@ static long nvram_misc_ioctl(struct file *file, unsigned int cmd, break; #ifdef CONFIG_PPC32 case IOC_NVRAM_SYNC: - if (ppc_md.nvram_sync != NULL) { - mutex_lock(&nvram_mutex); + if (ppc_md.nvram_sync) ppc_md.nvram_sync(); - mutex_unlock(&nvram_mutex); - } ret = 0; break; #endif @@ -324,11 +320,8 @@ static long nvram_misc_ioctl(struct file *file, unsigned int cmd, if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (arch_nvram_ops.initialize != NULL) { - mutex_lock(&nvram_mutex); + if (arch_nvram_ops.initialize) ret = arch_nvram_ops.initialize(); - mutex_unlock(&nvram_mutex); - } break; case NVRAM_SETCKS: /* just set checksum, contents unchanged (maybe useful after @@ -336,11 +329,8 @@ static long nvram_misc_ioctl(struct file *file, unsigned int cmd, if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (arch_nvram_ops.set_checksum != NULL) { - mutex_lock(&nvram_mutex); + if (arch_nvram_ops.set_checksum) ret = arch_nvram_ops.set_checksum(); - mutex_unlock(&nvram_mutex); - } break; #endif /* CONFIG_X86 || CONFIG_M68K */ } diff --git a/drivers/char/powernv-op-panel.c b/drivers/char/powernv-op-panel.c index 53467b0a6187..63175b765c90 100644 --- a/drivers/char/powernv-op-panel.c +++ b/drivers/char/powernv-op-panel.c @@ -167,7 +167,7 @@ static int oppanel_probe(struct platform_device *pdev) if (!oppanel_data) return -ENOMEM; - oppanel_lines = kcalloc(num_lines, sizeof(oppanel_line_t), GFP_KERNEL); + oppanel_lines = kzalloc_objs(oppanel_line_t, num_lines); if (!oppanel_lines) { rc = -ENOMEM; goto free_oppanel_data; diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index d1dfbd8d4d42..6da817b9849f 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -689,7 +689,7 @@ static int pp_open(struct inode *inode, struct file *file) if (minor >= PARPORT_MAX) return -ENXIO; - pp = kmalloc(sizeof(struct pp_struct), GFP_KERNEL); + pp = kmalloc_obj(struct pp_struct); if (!pp) return -ENOMEM; diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c index 23871cde41fb..c73f443077a4 100644 --- a/drivers/char/ps3flash.c +++ b/drivers/char/ps3flash.c @@ -361,7 +361,7 @@ static int ps3flash_probe(struct ps3_system_bus_device *_dev) ps3flash_dev = dev; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = kzalloc_obj(*priv); if (!priv) { error = -ENOMEM; goto fail; diff --git a/drivers/char/random.c b/drivers/char/random.c index 92cbd24a36d8..b4da1fb976c1 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -56,11 +56,7 @@ #include <linux/sched/isolation.h> #include <crypto/chacha.h> #include <crypto/blake2s.h> -#ifdef CONFIG_VDSO_GETRANDOM -#include <vdso/getrandom.h> #include <vdso/datapage.h> -#include <vdso/vsyscall.h> -#endif #include <asm/archrandom.h> #include <asm/processor.h> #include <asm/irq.h> @@ -96,8 +92,7 @@ static ATOMIC_NOTIFIER_HEAD(random_ready_notifier); /* Control how we warn userspace. */ static struct ratelimit_state urandom_warning = RATELIMIT_STATE_INIT_FLAGS("urandom_warning", HZ, 3, RATELIMIT_MSG_ON_RELEASE); -static int ratelimit_disable __read_mostly = - IS_ENABLED(CONFIG_WARN_ALL_UNSEEDED_RANDOM); +static int ratelimit_disable __read_mostly = 0; module_param_named(ratelimit_disable, ratelimit_disable, int, 0644); MODULE_PARM_DESC(ratelimit_disable, "Disable random ratelimit suppression"); @@ -168,12 +163,6 @@ int __cold execute_with_initialized_rng(struct notifier_block *nb) return ret; } -#define warn_unseeded_randomness() \ - if (IS_ENABLED(CONFIG_WARN_ALL_UNSEEDED_RANDOM) && !crng_ready()) \ - printk_deferred(KERN_NOTICE "random: %s called from %pS with crng_init=%d\n", \ - __func__, (void *)_RET_IP_, crng_init) - - /********************************************************************* * * Fast key erasure RNG, the "crng". @@ -259,8 +248,8 @@ static void crng_reseed(struct work_struct *work) u8 key[CHACHA_KEY_SIZE]; /* Immediately schedule the next reseeding, so that it fires sooner rather than later. */ - if (likely(system_unbound_wq)) - queue_delayed_work(system_unbound_wq, &next_reseed, crng_reseed_interval()); + if (likely(system_dfl_wq)) + queue_delayed_work(system_dfl_wq, &next_reseed, crng_reseed_interval()); extract_entropy(key, sizeof(key)); @@ -276,7 +265,7 @@ static void crng_reseed(struct work_struct *work) if (next_gen == ULONG_MAX) ++next_gen; WRITE_ONCE(base_crng.generation, next_gen); -#ifdef CONFIG_VDSO_GETRANDOM + /* base_crng.generation's invalid value is ULONG_MAX, while * vdso_k_rng_data->generation's invalid value is 0, so add one to the * former to arrive at the latter. Use smp_store_release so that this @@ -290,8 +279,9 @@ static void crng_reseed(struct work_struct *work) * because the vDSO side only checks whether the value changed, without * actually using or interpreting the value. */ - smp_store_release((unsigned long *)&vdso_k_rng_data->generation, next_gen + 1); -#endif + if (IS_ENABLED(CONFIG_VDSO_GETRANDOM)) + smp_store_release((unsigned long *)&vdso_k_rng_data->generation, next_gen + 1); + if (!static_branch_likely(&crng_is_ready)) crng_init = CRNG_READY; spin_unlock_irqrestore(&base_crng.lock, flags); @@ -309,11 +299,11 @@ static void crng_reseed(struct work_struct *work) * key value, at index 4, so the state should always be zeroed out * immediately after using in order to maintain forward secrecy. * If the state cannot be erased in a timely manner, then it is - * safer to set the random_data parameter to &chacha_state[4] so - * that this function overwrites it before returning. + * safer to set the random_data parameter to &chacha_state->x[4] + * so that this function overwrites it before returning. */ static void crng_fast_key_erasure(u8 key[CHACHA_KEY_SIZE], - u32 chacha_state[CHACHA_STATE_WORDS], + struct chacha_state *chacha_state, u8 *random_data, size_t random_data_len) { u8 first_block[CHACHA_BLOCK_SIZE]; @@ -321,8 +311,8 @@ static void crng_fast_key_erasure(u8 key[CHACHA_KEY_SIZE], BUG_ON(random_data_len > 32); chacha_init_consts(chacha_state); - memcpy(&chacha_state[4], key, CHACHA_KEY_SIZE); - memset(&chacha_state[12], 0, sizeof(u32) * 4); + memcpy(&chacha_state->x[4], key, CHACHA_KEY_SIZE); + memset(&chacha_state->x[12], 0, sizeof(u32) * 4); chacha20_block(chacha_state, first_block); memcpy(key, first_block, CHACHA_KEY_SIZE); @@ -335,7 +325,7 @@ static void crng_fast_key_erasure(u8 key[CHACHA_KEY_SIZE], * random data. It also returns up to 32 bytes on its own of random data * that may be used; random_data_len may not be greater than 32. */ -static void crng_make_state(u32 chacha_state[CHACHA_STATE_WORDS], +static void crng_make_state(struct chacha_state *chacha_state, u8 *random_data, size_t random_data_len) { unsigned long flags; @@ -395,7 +385,7 @@ static void crng_make_state(u32 chacha_state[CHACHA_STATE_WORDS], static void _get_random_bytes(void *buf, size_t len) { - u32 chacha_state[CHACHA_STATE_WORDS]; + struct chacha_state chacha_state; u8 tmp[CHACHA_BLOCK_SIZE]; size_t first_block_len; @@ -403,45 +393,44 @@ static void _get_random_bytes(void *buf, size_t len) return; first_block_len = min_t(size_t, 32, len); - crng_make_state(chacha_state, buf, first_block_len); + crng_make_state(&chacha_state, buf, first_block_len); len -= first_block_len; buf += first_block_len; while (len) { if (len < CHACHA_BLOCK_SIZE) { - chacha20_block(chacha_state, tmp); + chacha20_block(&chacha_state, tmp); memcpy(buf, tmp, len); memzero_explicit(tmp, sizeof(tmp)); break; } - chacha20_block(chacha_state, buf); - if (unlikely(chacha_state[12] == 0)) - ++chacha_state[13]; + chacha20_block(&chacha_state, buf); + if (unlikely(chacha_state.x[12] == 0)) + ++chacha_state.x[13]; len -= CHACHA_BLOCK_SIZE; buf += CHACHA_BLOCK_SIZE; } - memzero_explicit(chacha_state, sizeof(chacha_state)); + chacha_zeroize_state(&chacha_state); } /* * This returns random bytes in arbitrary quantities. The quality of the - * random bytes is good as /dev/urandom. In order to ensure that the + * random bytes is as good as /dev/urandom. In order to ensure that the * randomness provided by this function is okay, the function * wait_for_random_bytes() should be called and return 0 at least once * at any point prior. */ void get_random_bytes(void *buf, size_t len) { - warn_unseeded_randomness(); _get_random_bytes(buf, len); } EXPORT_SYMBOL(get_random_bytes); static ssize_t get_random_bytes_user(struct iov_iter *iter) { - u32 chacha_state[CHACHA_STATE_WORDS]; + struct chacha_state chacha_state; u8 block[CHACHA_BLOCK_SIZE]; size_t ret = 0, copied; @@ -453,21 +442,22 @@ static ssize_t get_random_bytes_user(struct iov_iter *iter) * bytes, in case userspace causes copy_to_iter() below to sleep * forever, so that we still retain forward secrecy in that case. */ - crng_make_state(chacha_state, (u8 *)&chacha_state[4], CHACHA_KEY_SIZE); + crng_make_state(&chacha_state, (u8 *)&chacha_state.x[4], + CHACHA_KEY_SIZE); /* * However, if we're doing a read of len <= 32, we don't need to * use chacha_state after, so we can simply return those bytes to * the user directly. */ if (iov_iter_count(iter) <= CHACHA_KEY_SIZE) { - ret = copy_to_iter(&chacha_state[4], CHACHA_KEY_SIZE, iter); + ret = copy_to_iter(&chacha_state.x[4], CHACHA_KEY_SIZE, iter); goto out_zero_chacha; } for (;;) { - chacha20_block(chacha_state, block); - if (unlikely(chacha_state[12] == 0)) - ++chacha_state[13]; + chacha20_block(&chacha_state, block); + if (unlikely(chacha_state.x[12] == 0)) + ++chacha_state.x[13]; copied = copy_to_iter(block, sizeof(block), iter); ret += copied; @@ -484,13 +474,13 @@ static ssize_t get_random_bytes_user(struct iov_iter *iter) memzero_explicit(block, sizeof(block)); out_zero_chacha: - memzero_explicit(chacha_state, sizeof(chacha_state)); + chacha_zeroize_state(&chacha_state); return ret ? ret : -EFAULT; } /* * Batched entropy returns random integers. The quality of the random - * number is good as /dev/urandom. In order to ensure that the randomness + * number is as good as /dev/urandom. In order to ensure that the randomness * provided by this function is okay, the function wait_for_random_bytes() * should be called and return 0 at least once at any point prior. */ @@ -522,8 +512,6 @@ type get_random_ ##type(void) \ struct batch_ ##type *batch; \ unsigned long next_gen; \ \ - warn_unseeded_randomness(); \ - \ if (!crng_ready()) { \ _get_random_bytes(&ret, sizeof(ret)); \ return ret; \ @@ -635,7 +623,7 @@ enum { }; static struct { - struct blake2s_state hash; + struct blake2s_ctx hash; spinlock_t lock; unsigned int init_bits; } input_pool = { @@ -700,7 +688,7 @@ static void extract_entropy(void *buf, size_t len) /* next_key = HASHPRF(seed, RDSEED || 0) */ block.counter = 0; - blake2s(next_key, (u8 *)&block, seed, sizeof(next_key), sizeof(block), sizeof(seed)); + blake2s(seed, sizeof(seed), (const u8 *)&block, sizeof(block), next_key, sizeof(next_key)); blake2s_init_key(&input_pool.hash, BLAKE2S_HASH_SIZE, next_key, sizeof(next_key)); spin_unlock_irqrestore(&input_pool.lock, flags); @@ -710,7 +698,7 @@ static void extract_entropy(void *buf, size_t len) i = min_t(size_t, len, BLAKE2S_HASH_SIZE); /* output = HASHPRF(seed, RDSEED || ++counter) */ ++block.counter; - blake2s(buf, (u8 *)&block, seed, i, sizeof(block), sizeof(seed)); + blake2s(seed, sizeof(seed), (const u8 *)&block, sizeof(block), buf, i); len -= i; buf += i; } @@ -726,6 +714,7 @@ static void __cold _credit_init_bits(size_t bits) static DECLARE_WORK(set_ready, crng_set_ready); unsigned int new, orig, add; unsigned long flags; + int m; if (!bits) return; @@ -739,18 +728,17 @@ static void __cold _credit_init_bits(size_t bits) if (orig < POOL_READY_BITS && new >= POOL_READY_BITS) { crng_reseed(NULL); /* Sets crng_init to CRNG_READY under base_crng.lock. */ - if (static_key_initialized && system_unbound_wq) - queue_work(system_unbound_wq, &set_ready); + if (system_dfl_wq) + queue_work(system_dfl_wq, &set_ready); atomic_notifier_call_chain(&random_ready_notifier, 0, NULL); -#ifdef CONFIG_VDSO_GETRANDOM - WRITE_ONCE(vdso_k_rng_data->is_ready, true); -#endif + if (IS_ENABLED(CONFIG_VDSO_GETRANDOM)) + WRITE_ONCE(vdso_k_rng_data->is_ready, true); wake_up_interruptible(&crng_init_wait); kill_fasync(&fasync, SIGIO, POLL_IN); pr_notice("crng init done\n"); - if (urandom_warning.missed) - pr_notice("%d urandom warning(s) missed due to ratelimiting\n", - urandom_warning.missed); + m = ratelimit_state_get_miss(&urandom_warning); + if (m) + pr_notice("%d urandom warning(s) missed due to ratelimiting\n", m); } else if (orig < POOL_EARLY_BITS && new >= POOL_EARLY_BITS) { spin_lock_irqsave(&base_crng.lock, flags); /* Check if crng_init is CRNG_EMPTY, to avoid race with crng_reseed(). */ @@ -792,7 +780,7 @@ static void __cold _credit_init_bits(size_t bits) * * add_bootloader_randomness() is called by bootloader drivers, such as EFI * and device tree, and credits its input depending on whether or not the - * command line option 'random.trust_bootloader'. + * command line option 'random.trust_bootloader' is set. * * add_vmfork_randomness() adds a unique (but not necessarily secret) ID * representing the current instance of a VM to the pool, without crediting, @@ -913,9 +901,8 @@ void __init random_init(void) add_latent_entropy(); /* - * If we were initialized by the cpu or bootloader before jump labels - * or workqueues are initialized, then we should enable the static - * branch here, where it's guaranteed that these have been initialized. + * If we were initialized by the cpu or bootloader before workqueues + * are initialized, then we should enable the static branch here. */ if (!static_branch_likely(&crng_is_ready) && crng_init >= CRNG_READY) crng_set_ready(NULL); @@ -1247,7 +1234,7 @@ void __cold rand_initialize_disk(struct gendisk *disk) * If kzalloc returns null, we just won't use that entropy * source. */ - state = kzalloc(sizeof(struct timer_rand_state), GFP_KERNEL); + state = kzalloc_obj(struct timer_rand_state); if (state) { state->last_time = INITIAL_JIFFIES; disk->random = state; @@ -1294,6 +1281,7 @@ static void __cold try_to_generate_entropy(void) struct entropy_timer_state *stack = PTR_ALIGN((void *)stack_bytes, SMP_CACHE_BYTES); unsigned int i, num_different = 0; unsigned long last = random_get_entropy(); + cpumask_var_t timer_cpus; int cpu = -1; for (i = 0; i < NUM_TRIAL_SAMPLES - 1; ++i) { @@ -1308,13 +1296,15 @@ static void __cold try_to_generate_entropy(void) atomic_set(&stack->samples, 0); timer_setup_on_stack(&stack->timer, entropy_timer, 0); + if (!alloc_cpumask_var(&timer_cpus, GFP_KERNEL)) + goto out; + while (!crng_ready() && !signal_pending(current)) { /* * Check !timer_pending() and then ensure that any previous callback has finished - * executing by checking try_to_del_timer_sync(), before queueing the next one. + * executing by checking timer_delete_sync_try(), before queueing the next one. */ - if (!timer_pending(&stack->timer) && try_to_del_timer_sync(&stack->timer) >= 0) { - struct cpumask timer_cpus; + if (!timer_pending(&stack->timer) && timer_delete_sync_try(&stack->timer) >= 0) { unsigned int num_cpus; /* @@ -1324,19 +1314,19 @@ static void __cold try_to_generate_entropy(void) preempt_disable(); /* Only schedule callbacks on timer CPUs that are online. */ - cpumask_and(&timer_cpus, housekeeping_cpumask(HK_TYPE_TIMER), cpu_online_mask); - num_cpus = cpumask_weight(&timer_cpus); + cpumask_and(timer_cpus, housekeeping_cpumask(HK_TYPE_TIMER), cpu_online_mask); + num_cpus = cpumask_weight(timer_cpus); /* In very bizarre case of misconfiguration, fallback to all online. */ if (unlikely(num_cpus == 0)) { - timer_cpus = *cpu_online_mask; - num_cpus = cpumask_weight(&timer_cpus); + *timer_cpus = *cpu_online_mask; + num_cpus = cpumask_weight(timer_cpus); } /* Basic CPU round-robin, which avoids the current CPU. */ do { - cpu = cpumask_next(cpu, &timer_cpus); + cpu = cpumask_next(cpu, timer_cpus); if (cpu >= nr_cpu_ids) - cpu = cpumask_first(&timer_cpus); + cpu = cpumask_first(timer_cpus); } while (cpu == smp_processor_id() && num_cpus > 1); /* Expiring the timer at `jiffies` means it's the next tick. */ @@ -1352,8 +1342,10 @@ static void __cold try_to_generate_entropy(void) } mix_pool_bytes(&stack->entropy, sizeof(stack->entropy)); - del_timer_sync(&stack->timer); - destroy_timer_on_stack(&stack->timer); + free_cpumask_var(timer_cpus); +out: + timer_delete_sync(&stack->timer); + timer_destroy_on_stack(&stack->timer); } @@ -1466,7 +1458,7 @@ static ssize_t urandom_read_iter(struct kiocb *kiocb, struct iov_iter *iter) if (!crng_ready()) { if (!ratelimit_disable && maxwarn <= 0) - ++urandom_warning.missed; + ratelimit_state_inc_miss(&urandom_warning); else if (ratelimit_disable || __ratelimit(&urandom_warning)) { --maxwarn; pr_notice("%s: uninitialized urandom read (%zu bytes read)\n", diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index f887569fd3d0..9309cfb935be 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -37,6 +37,8 @@ #include <linux/kfifo.h> #include <linux/platform_device.h> #include <linux/gfp.h> +#include <linux/string.h> +#include <linux/string_choices.h> #include <linux/uaccess.h> #include <asm/io.h> @@ -1114,15 +1116,21 @@ static int sonypi_disable(void) } #ifdef CONFIG_ACPI -static int sonypi_acpi_add(struct acpi_device *device) +static int sonypi_acpi_probe(struct platform_device *pdev) { + struct acpi_device *device; + + device = ACPI_COMPANION(&pdev->dev); + if (!device) + return -ENODEV; + sonypi_acpi_device = device; - strcpy(acpi_device_name(device), "Sony laptop hotkeys"); - strcpy(acpi_device_class(device), "sony/hotkey"); + strscpy(acpi_device_name(device), "Sony laptop hotkeys"); + strscpy(acpi_device_class(device), "sony/hotkey"); return 0; } -static void sonypi_acpi_remove(struct acpi_device *device) +static void sonypi_acpi_remove(struct platform_device *pdev) { sonypi_acpi_device = NULL; } @@ -1132,13 +1140,12 @@ static const struct acpi_device_id sonypi_device_ids[] = { {"", 0}, }; -static struct acpi_driver sonypi_acpi_driver = { - .name = "sonypi", - .class = "hkey", - .ids = sonypi_device_ids, - .ops = { - .add = sonypi_acpi_add, - .remove = sonypi_acpi_remove, +static struct platform_driver sonypi_acpi_driver = { + .probe = sonypi_acpi_probe, + .remove = sonypi_acpi_remove, + .driver = { + .name = "sonypi_acpi", + .acpi_match_table = sonypi_device_ids, }, }; #endif @@ -1268,12 +1275,12 @@ static void sonypi_display_info(void) "compat = %s, mask = 0x%08lx, useinput = %s, acpi = %s\n", sonypi_device.model, verbose, - fnkeyinit ? "on" : "off", - camera ? "on" : "off", - compat ? "on" : "off", + str_on_off(fnkeyinit), + str_on_off(camera), + str_on_off(compat), mask, - useinput ? "on" : "off", - SONYPI_ACPI_ACTIVE ? "on" : "off"); + str_on_off(useinput), + str_on_off(SONYPI_ACPI_ACTIVE)); printk(KERN_INFO "sonypi: enabled at irq=%d, port1=0x%x, port2=0x%x\n", sonypi_device.irq, sonypi_device.ioport1, sonypi_device.ioport2); @@ -1517,8 +1524,8 @@ static int __init sonypi_init(void) goto err_free_device; #ifdef CONFIG_ACPI - if (acpi_bus_register_driver(&sonypi_acpi_driver) >= 0) - acpi_driver_registered = 1; + error = platform_driver_register(&sonypi_acpi_driver); + acpi_driver_registered = !error; #endif return 0; @@ -1534,7 +1541,7 @@ static void __exit sonypi_exit(void) { #ifdef CONFIG_ACPI if (acpi_driver_registered) - acpi_bus_unregister_driver(&sonypi_acpi_driver); + platform_driver_unregister(&sonypi_acpi_driver); #endif platform_device_unregister(sonypi_platform_device); platform_driver_unregister(&sonypi_driver); diff --git a/drivers/char/tlclk.c b/drivers/char/tlclk.c index 377bebf6c925..dd45fe5eb6f2 100644 --- a/drivers/char/tlclk.c +++ b/drivers/char/tlclk.c @@ -42,7 +42,7 @@ #include <linux/sysfs.h> #include <linux/device.h> #include <linux/miscdevice.h> -#include <linux/platform_device.h> +#include <linux/device/faux.h> #include <asm/io.h> /* inb/outb */ #include <linux/uaccess.h> @@ -264,6 +264,7 @@ static ssize_t tlclk_read(struct file *filp, char __user *buf, size_t count, } static const struct file_operations tlclk_fops = { + .owner = THIS_MODULE, .read = tlclk_read, .open = tlclk_open, .release = tlclk_release, @@ -742,7 +743,7 @@ static ssize_t store_reset (struct device *d, static DEVICE_ATTR(reset, (S_IWUSR|S_IWGRP), NULL, store_reset); -static struct attribute *tlclk_sysfs_entries[] = { +static struct attribute *tlclk_attrs[] = { &dev_attr_current_ref.attr, &dev_attr_telclock_version.attr, &dev_attr_alarms.attr, @@ -766,13 +767,9 @@ static struct attribute *tlclk_sysfs_entries[] = { &dev_attr_reset.attr, NULL }; +ATTRIBUTE_GROUPS(tlclk); -static const struct attribute_group tlclk_attribute_group = { - .name = NULL, /* put in device directory */ - .attrs = tlclk_sysfs_entries, -}; - -static struct platform_device *tlclk_device; +static struct faux_device *tlclk_device; static int __init tlclk_init(void) { @@ -780,7 +777,7 @@ static int __init tlclk_init(void) telclk_interrupt = (inb(TLCLK_REG7) & 0x0f); - alarm_events = kzalloc( sizeof(struct tlclk_alarms), GFP_KERNEL); + alarm_events = kzalloc_obj(struct tlclk_alarms); if (!alarm_events) { ret = -ENOMEM; goto out1; @@ -817,24 +814,13 @@ static int __init tlclk_init(void) goto out3; } - tlclk_device = platform_device_register_simple("telco_clock", - -1, NULL, 0); - if (IS_ERR(tlclk_device)) { - printk(KERN_ERR "tlclk: platform_device_register failed.\n"); - ret = PTR_ERR(tlclk_device); + tlclk_device = faux_device_create_with_groups("telco_clock", NULL, NULL, tlclk_groups); + if (!tlclk_device) { + ret = -ENODEV; goto out4; } - ret = sysfs_create_group(&tlclk_device->dev.kobj, - &tlclk_attribute_group); - if (ret) { - printk(KERN_ERR "tlclk: failed to create sysfs device attributes.\n"); - goto out5; - } - return 0; -out5: - platform_device_unregister(tlclk_device); out4: misc_deregister(&tlclk_miscdev); out3: @@ -848,13 +834,15 @@ out1: static void __exit tlclk_cleanup(void) { - sysfs_remove_group(&tlclk_device->dev.kobj, &tlclk_attribute_group); - platform_device_unregister(tlclk_device); + faux_device_destroy(tlclk_device); misc_deregister(&tlclk_miscdev); unregister_chrdev(tlclk_major, "telco_clock"); + got_event = 1; + wake_up_all(&wq); + release_region(TLCLK_BASE, 8); - del_timer_sync(&switchover_timer); + timer_delete_sync(&switchover_timer); kfree(alarm_events); } @@ -872,7 +860,7 @@ static void switchover_timeout(struct timer_list *unused) } /* Alarm processing is done, wake up read task */ - del_timer(&switchover_timer); + timer_delete(&switchover_timer); got_event = 1; wake_up(&wq); } diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index 0fc9a510e059..8a8f692b6088 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -29,10 +29,11 @@ if TCG_TPM config TCG_TPM2_HMAC bool "Use HMAC and encrypted transactions on the TPM bus" - default X86_64 + default n select CRYPTO_ECDH select CRYPTO_LIB_AESCFB select CRYPTO_LIB_SHA256 + select CRYPTO_LIB_UTILS help Setting this causes us to deploy a scheme which uses request and response HMACs in addition to encryption for @@ -189,6 +190,15 @@ config TCG_IBMVTPM will be accessible from within Linux. To compile this driver as a module, choose M here; the module will be called tpm_ibmvtpm. +config TCG_LOONGSON + tristate "Loongson TPM Interface" + depends on MFD_LOONGSON_SE + help + If you want to make Loongson TPM support available, say Yes and + it will be accessible from within Linux. To compile this + driver as a module, choose M here; the module will be called + tpm_loongson. + config TCG_XEN tristate "XEN TPM Interface" depends on TCG_TPM && XEN @@ -210,6 +220,15 @@ config TCG_CRB from within Linux. To compile this driver as a module, choose M here; the module will be called tpm_crb. +config TCG_ARM_CRB_FFA + tristate "TPM CRB over Arm FF-A Transport" + depends on ARM_FFA_TRANSPORT && TCG_CRB + default TCG_CRB + help + If the Arm FF-A transport is used to access the TPM say Yes. + To compile this driver as a module, choose M here; the module + will be called tpm_crb_ffa. + config TCG_VTPM_PROXY tristate "VTPM Proxy Interface" depends on TCG_TPM @@ -225,5 +244,15 @@ config TCG_FTPM_TEE help This driver proxies for firmware TPM running in TEE. +config TCG_SVSM + tristate "SNP SVSM vTPM interface" + depends on AMD_MEM_ENCRYPT + help + This is a driver for the AMD SVSM vTPM protocol that a SEV-SNP guest + OS can use to discover and talk to a vTPM emulated by the Secure VM + Service Module (SVSM) in the guest context, but at a more privileged + level (usually VMPL0). To compile this driver as a module, choose M + here; the module will be called tpm_svsm. + source "drivers/char/tpm/st33zp24/Kconfig" endif # TCG_TPM diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index 9bb142c75243..5b5cdc0d32e4 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -42,5 +42,8 @@ obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o obj-$(CONFIG_TCG_TIS_ST33ZP24) += st33zp24/ obj-$(CONFIG_TCG_XEN) += xen-tpmfront.o obj-$(CONFIG_TCG_CRB) += tpm_crb.o +obj-$(CONFIG_TCG_ARM_CRB_FFA) += tpm_crb_ffa.o obj-$(CONFIG_TCG_VTPM_PROXY) += tpm_vtpm_proxy.o obj-$(CONFIG_TCG_FTPM_TEE) += tpm_ftpm_tee.o +obj-$(CONFIG_TCG_SVSM) += tpm_svsm.o +obj-$(CONFIG_TCG_LOONGSON) += tpm_loongson.o diff --git a/drivers/char/tpm/eventlog/common.c b/drivers/char/tpm/eventlog/common.c index 4c0bbba64ee5..691813d2a5a2 100644 --- a/drivers/char/tpm/eventlog/common.c +++ b/drivers/char/tpm/eventlog/common.c @@ -32,7 +32,7 @@ static int tpm_bios_measurements_open(struct inode *inode, struct tpm_chip *chip; inode_lock(inode); - if (!inode->i_private) { + if (!inode->i_nlink) { inode_unlock(inode); return -ENODEV; } @@ -105,7 +105,7 @@ static int tpm_read_log(struct tpm_chip *chip) void tpm_bios_log_setup(struct tpm_chip *chip) { const char *name = dev_name(&chip->dev); - unsigned int cnt; + struct dentry *dentry; int log_version; int rc = 0; @@ -117,14 +117,12 @@ void tpm_bios_log_setup(struct tpm_chip *chip) return; log_version = rc; - cnt = 0; - chip->bios_dir[cnt] = securityfs_create_dir(name, NULL); + chip->bios_dir = securityfs_create_dir(name, NULL); /* NOTE: securityfs_create_dir can return ENODEV if securityfs is * compiled out. The caller should ignore the ENODEV return code. */ - if (IS_ERR(chip->bios_dir[cnt])) - goto err; - cnt++; + if (IS_ERR(chip->bios_dir)) + return; chip->bin_log_seqops.chip = chip; if (log_version == EFI_TCG2_EVENT_LOG_FORMAT_TCG_2) @@ -135,14 +133,13 @@ void tpm_bios_log_setup(struct tpm_chip *chip) &tpm1_binary_b_measurements_seqops; - chip->bios_dir[cnt] = + dentry = securityfs_create_file("binary_bios_measurements", - 0440, chip->bios_dir[0], + 0440, chip->bios_dir, (void *)&chip->bin_log_seqops, &tpm_bios_measurements_ops); - if (IS_ERR(chip->bios_dir[cnt])) + if (IS_ERR(dentry)) goto err; - cnt++; if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) { @@ -150,42 +147,23 @@ void tpm_bios_log_setup(struct tpm_chip *chip) chip->ascii_log_seqops.seqops = &tpm1_ascii_b_measurements_seqops; - chip->bios_dir[cnt] = + dentry = securityfs_create_file("ascii_bios_measurements", - 0440, chip->bios_dir[0], + 0440, chip->bios_dir, (void *)&chip->ascii_log_seqops, &tpm_bios_measurements_ops); - if (IS_ERR(chip->bios_dir[cnt])) + if (IS_ERR(dentry)) goto err; - cnt++; } return; err: - chip->bios_dir[cnt] = NULL; tpm_bios_log_teardown(chip); return; } void tpm_bios_log_teardown(struct tpm_chip *chip) { - int i; - struct inode *inode; - - /* securityfs_remove currently doesn't take care of handling sync - * between removal and opening of pseudo files. To handle this, a - * workaround is added by making i_private = NULL here during removal - * and to check it during open(), both within inode_lock()/unlock(). - * This design ensures that open() either safely gets kref or fails. - */ - for (i = (TPM_NUM_EVENT_LOG_FILES - 1); i >= 0; i--) { - if (chip->bios_dir[i]) { - inode = d_inode(chip->bios_dir[i]); - inode_lock(inode); - inode->i_private = NULL; - inode_unlock(inode); - securityfs_remove(chip->bios_dir[i]); - } - } + securityfs_remove(chip->bios_dir); } diff --git a/drivers/char/tpm/eventlog/of.c b/drivers/char/tpm/eventlog/of.c index 930fe43d5daf..92cec9722ee4 100644 --- a/drivers/char/tpm/eventlog/of.c +++ b/drivers/char/tpm/eventlog/of.c @@ -24,16 +24,10 @@ static int tpm_read_log_memory_region(struct tpm_chip *chip) { - struct device_node *node; struct resource res; int rc; - node = of_parse_phandle(chip->dev.parent->of_node, "memory-region", 0); - if (!node) - return -ENODEV; - - rc = of_address_to_resource(node, 0, &res); - of_node_put(node); + rc = of_reserved_mem_region_to_resource(chip->dev.parent->of_node, 0, &res); if (rc) return rc; diff --git a/drivers/char/tpm/eventlog/tpm1.c b/drivers/char/tpm/eventlog/tpm1.c index 12ee42a31c71..0397e3361020 100644 --- a/drivers/char/tpm/eventlog/tpm1.c +++ b/drivers/char/tpm/eventlog/tpm1.c @@ -236,12 +236,12 @@ static int tpm1_binary_bios_measurements_show(struct seq_file *m, void *v) temp_ptr = (char *) &temp_event; - for (i = 0; i < (sizeof(struct tcpa_event) - 1) ; i++) + for (i = 0; i < sizeof(struct tcpa_event); i++) seq_putc(m, temp_ptr[i]); temp_ptr = (char *) v; - for (i = (sizeof(struct tcpa_event) - 1); + for (i = sizeof(struct tcpa_event); i < (sizeof(struct tcpa_event) + temp_event.event_size); i++) seq_putc(m, temp_ptr[i]); @@ -257,11 +257,8 @@ static int tpm1_ascii_bios_measurements_show(struct seq_file *m, void *v) (unsigned char *)(v + sizeof(struct tcpa_event)); eventname = kmalloc(MAX_TEXT_EVENT, GFP_KERNEL); - if (!eventname) { - printk(KERN_ERR "%s: ERROR - No Memory for event name\n ", - __func__); - return -EFAULT; - } + if (!eventname) + return -ENOMEM; /* 1st: PCR */ seq_printf(m, "%2d ", do_endian_conversion(event->pcr_index)); diff --git a/drivers/char/tpm/st33zp24/st33zp24.c b/drivers/char/tpm/st33zp24/st33zp24.c index c0771980bc2f..e2b7451ea7cc 100644 --- a/drivers/char/tpm/st33zp24/st33zp24.c +++ b/drivers/char/tpm/st33zp24/st33zp24.c @@ -300,7 +300,7 @@ static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id) * send TPM commands through the I2C bus. */ static int st33zp24_send(struct tpm_chip *chip, unsigned char *buf, - size_t len) + size_t bufsiz, size_t len) { struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev); u32 status, i, size, ordinal; @@ -328,8 +328,10 @@ static int st33zp24_send(struct tpm_chip *chip, unsigned char *buf, for (i = 0; i < len - 1;) { burstcnt = get_burstcount(chip); - if (burstcnt < 0) - return burstcnt; + if (burstcnt < 0) { + ret = burstcnt; + goto out_err; + } size = min_t(int, len - i - 1, burstcnt); ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_DATA_FIFO, buf + i, size); diff --git a/drivers/char/tpm/tpm-buf.c b/drivers/char/tpm/tpm-buf.c index e49a19fea3bd..dc882fc9fa9e 100644 --- a/drivers/char/tpm/tpm-buf.c +++ b/drivers/char/tpm/tpm-buf.c @@ -201,7 +201,7 @@ static void tpm_buf_read(struct tpm_buf *buf, off_t *offset, size_t count, void */ u8 tpm_buf_read_u8(struct tpm_buf *buf, off_t *offset) { - u8 value; + u8 value = 0; tpm_buf_read(buf, offset, sizeof(value), &value); @@ -218,7 +218,7 @@ EXPORT_SYMBOL_GPL(tpm_buf_read_u8); */ u16 tpm_buf_read_u16(struct tpm_buf *buf, off_t *offset) { - u16 value; + u16 value = 0; tpm_buf_read(buf, offset, sizeof(value), &value); @@ -235,7 +235,7 @@ EXPORT_SYMBOL_GPL(tpm_buf_read_u16); */ u32 tpm_buf_read_u32(struct tpm_buf *buf, off_t *offset) { - u32 value; + u32 value = 0; tpm_buf_read(buf, offset, sizeof(value), &value); diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 7df7abaf3e52..12b7394b34bd 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -168,6 +168,11 @@ int tpm_try_get_ops(struct tpm_chip *chip) goto out_ops; mutex_lock(&chip->tpm_mutex); + + /* tmp_chip_start may issue IO that is denied while suspended */ + if (chip->flags & TPM_CHIP_FLAG_SUSPENDED) + goto out_lock; + rc = tpm_chip_start(chip); if (rc) goto out_lock; @@ -226,42 +231,6 @@ struct tpm_chip *tpm_default_chip(void) EXPORT_SYMBOL_GPL(tpm_default_chip); /** - * tpm_find_get_ops() - find and reserve a TPM chip - * @chip: a &struct tpm_chip instance, %NULL for the default chip - * - * Finds a TPM chip and reserves its class device and operations. The chip must - * be released with tpm_put_ops() after use. - * This function is for internal use only. It supports existing TPM callers - * by accepting NULL, but those callers should be converted to pass in a chip - * directly. - * - * Return: - * A reserved &struct tpm_chip instance. - * %NULL if a chip is not found. - * %NULL if the chip is not available. - */ -struct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip) -{ - int rc; - - if (chip) { - if (!tpm_try_get_ops(chip)) - return chip; - return NULL; - } - - chip = tpm_default_chip(); - if (!chip) - return NULL; - rc = tpm_try_get_ops(chip); - /* release additional reference we got from tpm_default_chip() */ - put_device(&chip->dev); - if (rc) - return NULL; - return chip; -} - -/** * tpm_dev_release() - free chip memory and the device number * @dev: the character device for the TPM chip * @@ -277,9 +246,8 @@ static void tpm_dev_release(struct device *dev) kfree(chip->work_space.context_buf); kfree(chip->work_space.session_buf); - kfree(chip->allocated_banks); #ifdef CONFIG_TCG_TPM2_HMAC - kfree(chip->auth); + kfree_sensitive(chip->auth); #endif kfree(chip); } @@ -300,6 +268,7 @@ int tpm_class_shutdown(struct device *dev) down_write(&chip->ops_sem); if (chip->flags & TPM_CHIP_FLAG_TPM2) { if (!tpm_chip_start(chip)) { + tpm2_end_auth_session(chip); tpm2_shutdown(chip, TPM2_SU_CLEAR); tpm_chip_stop(chip); } @@ -326,7 +295,7 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev, struct tpm_chip *chip; int rc; - chip = kzalloc(sizeof(*chip), GFP_KERNEL); + chip = kzalloc_obj(*chip); if (chip == NULL) return ERR_PTR(-ENOMEM); diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c index 48ff87444f85..f942c0c8e402 100644 --- a/drivers/char/tpm/tpm-dev-common.c +++ b/drivers/char/tpm/tpm-dev-common.c @@ -91,7 +91,7 @@ out: static void user_reader_timeout(struct timer_list *t) { - struct file_priv *priv = from_timer(priv, t, user_read_timer); + struct file_priv *priv = timer_container_of(priv, t, user_read_timer); pr_warn("TPM user space timeout is deprecated (pid=%d)\n", task_tgid_nr(current)); @@ -160,7 +160,7 @@ ssize_t tpm_common_read(struct file *file, char __user *buf, out: if (!priv->response_length) { *off = 0; - del_timer_sync(&priv->user_read_timer); + timer_delete_sync(&priv->user_read_timer); flush_work(&priv->timeout_work); } mutex_unlock(&priv->buffer_mutex); @@ -267,7 +267,7 @@ __poll_t tpm_common_poll(struct file *file, poll_table *wait) void tpm_common_release(struct file *file, struct file_priv *priv) { flush_work(&priv->async_work); - del_timer_sync(&priv->user_read_timer); + timer_delete_sync(&priv->user_read_timer); flush_work(&priv->timeout_work); file->private_data = NULL; priv->response_length = 0; @@ -275,7 +275,8 @@ void tpm_common_release(struct file *file, struct file_priv *priv) int __init tpm_dev_common_init(void) { - tpm_dev_wq = alloc_workqueue("tpm_dev_wq", WQ_MEM_RECLAIM, 0); + tpm_dev_wq = alloc_workqueue("tpm_dev_wq", WQ_MEM_RECLAIM | WQ_PERCPU, + 0); return !tpm_dev_wq ? -ENOMEM : 0; } diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c index 97c94b5e9340..74488f0a7b78 100644 --- a/drivers/char/tpm/tpm-dev.c +++ b/drivers/char/tpm/tpm-dev.c @@ -30,13 +30,13 @@ static int tpm_open(struct inode *inode, struct file *file) return -EBUSY; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = kzalloc_obj(*priv); if (priv == NULL) goto out; tpm_common_open(file, chip, priv, NULL); - return 0; + return nonseekable_open(inode, file); out: clear_bit(0, &chip->is_open); diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index b1daa0d7b341..f745a098908b 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -52,12 +52,43 @@ MODULE_PARM_DESC(suspend_pcr, unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal) { if (chip->flags & TPM_CHIP_FLAG_TPM2) - return tpm2_calc_ordinal_duration(chip, ordinal); + return tpm2_calc_ordinal_duration(ordinal); else return tpm1_calc_ordinal_duration(chip, ordinal); } EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration); +static void tpm_chip_cancel(struct tpm_chip *chip) +{ + if (!chip->ops->cancel) + return; + + chip->ops->cancel(chip); +} + +static u8 tpm_chip_status(struct tpm_chip *chip) +{ + if (!chip->ops->status) + return 0; + + return chip->ops->status(chip); +} + +static bool tpm_chip_req_canceled(struct tpm_chip *chip, u8 status) +{ + if (!chip->ops->req_canceled) + return false; + + return chip->ops->req_canceled(chip, status); +} + +static bool tpm_transmit_completed(u8 status, struct tpm_chip *chip) +{ + u8 status_masked = status & chip->ops->req_complete_mask; + + return status_masked == chip->ops->req_complete_val; +} + static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz) { struct tpm_header *header = buf; @@ -82,7 +113,7 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz) return -E2BIG; } - rc = chip->ops->send(chip, buf, count); + rc = chip->ops->send(chip, buf, bufsiz, count); if (rc < 0) { if (rc != -EPIPE) dev_err(&chip->dev, @@ -90,8 +121,19 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz) return rc; } - /* A sanity check. send() should just return zero on success e.g. - * not the command length. + /* + * Synchronous devices return the response directly during the send() + * call in the same buffer. + */ + if (chip->flags & TPM_CHIP_FLAG_SYNC) { + len = rc; + rc = 0; + goto out_sync; + } + + /* + * A sanity check. send() of asynchronous devices should just return + * zero on success e.g. not the command length. */ if (rc > 0) { dev_warn(&chip->dev, @@ -104,12 +146,11 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz) stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal); do { - u8 status = chip->ops->status(chip); - if ((status & chip->ops->req_complete_mask) == - chip->ops->req_complete_val) + u8 status = tpm_chip_status(chip); + if (tpm_transmit_completed(status, chip)) goto out_recv; - if (chip->ops->req_canceled(chip, status)) { + if (tpm_chip_req_canceled(chip, status)) { dev_err(&chip->dev, "Operation Canceled\n"); return -ECANCELED; } @@ -118,7 +159,14 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz) rmb(); } while (time_before(jiffies, stop)); - chip->ops->cancel(chip); + /* + * Check for completion one more time, just in case the device reported + * it while the driver was sleeping in the busy loop above. + */ + if (tpm_transmit_completed(tpm_chip_status(chip), chip)) + goto out_recv; + + tpm_chip_cancel(chip); dev_err(&chip->dev, "Operation Timed out\n"); return -ETIME; @@ -127,7 +175,10 @@ out_recv: if (len < 0) { rc = len; dev_err(&chip->dev, "tpm_transmit: tpm_recv: error %d\n", rc); - } else if (len < TPM_HEADER_SIZE || len != be32_to_cpu(header->length)) + return rc; + } +out_sync: + if (len < TPM_HEADER_SIZE || len != be32_to_cpu(header->length)) rc = -EFAULT; return rc ? rc : len; @@ -262,10 +313,13 @@ int tpm_is_tpm2(struct tpm_chip *chip) { int rc; - chip = tpm_find_get_ops(chip); if (!chip) return -ENODEV; + rc = tpm_try_get_ops(chip); + if (rc) + return rc; + rc = (chip->flags & TPM_CHIP_FLAG_TPM2) != 0; tpm_put_ops(chip); @@ -287,10 +341,13 @@ int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx, { int rc; - chip = tpm_find_get_ops(chip); if (!chip) return -ENODEV; + rc = tpm_try_get_ops(chip); + if (rc) + return rc; + if (chip->flags & TPM_CHIP_FLAG_TPM2) rc = tpm2_pcr_read(chip, pcr_idx, digest, NULL); else @@ -318,10 +375,13 @@ int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, int rc; int i; - chip = tpm_find_get_ops(chip); if (!chip) return -ENODEV; + rc = tpm_try_get_ops(chip); + if (rc) + return rc; + for (i = 0; i < chip->nr_allocated_banks; i++) { if (digests[i].alg_id != chip->allocated_banks[i].alg_id) { rc = -EINVAL; @@ -441,22 +501,18 @@ int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max) if (!out || max > TPM_MAX_RNG_DATA) return -EINVAL; - chip = tpm_find_get_ops(chip); if (!chip) return -ENODEV; - /* Give back zero bytes, as TPM chip has not yet fully resumed: */ - if (chip->flags & TPM_CHIP_FLAG_SUSPENDED) { - rc = 0; - goto out; - } + rc = tpm_try_get_ops(chip); + if (rc) + return rc; if (chip->flags & TPM_CHIP_FLAG_TPM2) rc = tpm2_get_random(chip, out, max); else rc = tpm1_get_random(chip, out, max); -out: tpm_put_ops(chip); return rc; } diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 7bb87fa5f7a1..87d68ddf270a 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -20,6 +20,7 @@ #include <linux/module.h> #include <linux/delay.h> +#include <linux/hex.h> #include <linux/mutex.h> #include <linux/sched.h> #include <linux/platform_device.h> @@ -267,7 +268,6 @@ static inline void tpm_msleep(unsigned int delay_msec) int tpm_chip_bootstrap(struct tpm_chip *chip); int tpm_chip_start(struct tpm_chip *chip); void tpm_chip_stop(struct tpm_chip *chip); -struct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip); struct tpm_chip *tpm_chip_alloc(struct device *dev, const struct tpm_class_ops *ops); @@ -299,7 +299,7 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip); int tpm2_auto_startup(struct tpm_chip *chip); void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type); -unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal); +unsigned long tpm2_calc_ordinal_duration(u32 ordinal); int tpm2_probe(struct tpm_chip *chip); int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip); int tpm2_find_cc(struct tpm_chip *chip, u32 cc); diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c index cf64c7385105..b49a790f1bd5 100644 --- a/drivers/char/tpm/tpm1-cmd.c +++ b/drivers/char/tpm/tpm1-cmd.c @@ -799,11 +799,6 @@ int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr) */ int tpm1_get_pcr_allocation(struct tpm_chip *chip) { - chip->allocated_banks = kcalloc(1, sizeof(*chip->allocated_banks), - GFP_KERNEL); - if (!chip->allocated_banks) - return -ENOMEM; - chip->allocated_banks[0].alg_id = TPM_ALG_SHA1; chip->allocated_banks[0].digest_size = hash_digest_size[HASH_ALGO_SHA1]; chip->allocated_banks[0].crypto_id = HASH_ALGO_SHA1; diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index dfdcbd009720..52ee350da867 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -11,14 +11,17 @@ * used by the kernel internally. */ +#include "linux/dev_printk.h" +#include "linux/tpm.h" #include "tpm.h" #include <crypto/hash_info.h> +#include <linux/unaligned.h> static bool disable_pcr_integrity; module_param(disable_pcr_integrity, bool, 0444); MODULE_PARM_DESC(disable_pcr_integrity, "Disable integrity protection of TPM2_PCR_Extend"); -static struct tpm2_hash tpm2_hash_map[] = { +static const struct tpm2_hash tpm2_hash_map[] = { {HASH_ALGO_SHA1, TPM_ALG_SHA1}, {HASH_ALGO_SHA256, TPM_ALG_SHA256}, {HASH_ALGO_SHA384, TPM_ALG_SHA384}, @@ -26,122 +29,71 @@ static struct tpm2_hash tpm2_hash_map[] = { {HASH_ALGO_SM3_256, TPM_ALG_SM3_256}, }; +int tpm2_find_hash_alg(unsigned int crypto_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(tpm2_hash_map); i++) + if (crypto_id == tpm2_hash_map[i].crypto_id) + return tpm2_hash_map[i].tpm_id; + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(tpm2_find_hash_alg); + int tpm2_get_timeouts(struct tpm_chip *chip) { - /* Fixed timeouts for TPM2 */ chip->timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A); chip->timeout_b = msecs_to_jiffies(TPM2_TIMEOUT_B); chip->timeout_c = msecs_to_jiffies(TPM2_TIMEOUT_C); chip->timeout_d = msecs_to_jiffies(TPM2_TIMEOUT_D); - - /* PTP spec timeouts */ - chip->duration[TPM_SHORT] = msecs_to_jiffies(TPM2_DURATION_SHORT); - chip->duration[TPM_MEDIUM] = msecs_to_jiffies(TPM2_DURATION_MEDIUM); - chip->duration[TPM_LONG] = msecs_to_jiffies(TPM2_DURATION_LONG); - - /* Key creation commands long timeouts */ - chip->duration[TPM_LONG_LONG] = - msecs_to_jiffies(TPM2_DURATION_LONG_LONG); - chip->flags |= TPM_CHIP_FLAG_HAVE_TIMEOUTS; - return 0; } -/** - * tpm2_ordinal_duration_index() - returns an index to the chip duration table - * @ordinal: TPM command ordinal. - * - * The function returns an index to the chip duration table - * (enum tpm_duration), that describes the maximum amount of - * time the chip could take to return the result for a particular ordinal. - * - * The values of the MEDIUM, and LONG durations are taken - * from the PC Client Profile (PTP) specification (750, 2000 msec) - * - * LONG_LONG is for commands that generates keys which empirically takes - * a longer time on some systems. - * - * Return: - * * TPM_MEDIUM - * * TPM_LONG - * * TPM_LONG_LONG - * * TPM_UNDEFINED +/* + * Contains the maximum durations in milliseconds for TPM2 commands. */ -static u8 tpm2_ordinal_duration_index(u32 ordinal) -{ - switch (ordinal) { - /* Startup */ - case TPM2_CC_STARTUP: /* 144 */ - return TPM_MEDIUM; - - case TPM2_CC_SELF_TEST: /* 143 */ - return TPM_LONG; - - case TPM2_CC_GET_RANDOM: /* 17B */ - return TPM_LONG; - - case TPM2_CC_SEQUENCE_UPDATE: /* 15C */ - return TPM_MEDIUM; - case TPM2_CC_SEQUENCE_COMPLETE: /* 13E */ - return TPM_MEDIUM; - case TPM2_CC_EVENT_SEQUENCE_COMPLETE: /* 185 */ - return TPM_MEDIUM; - case TPM2_CC_HASH_SEQUENCE_START: /* 186 */ - return TPM_MEDIUM; - - case TPM2_CC_VERIFY_SIGNATURE: /* 177 */ - return TPM_LONG_LONG; - - case TPM2_CC_PCR_EXTEND: /* 182 */ - return TPM_MEDIUM; - - case TPM2_CC_HIERARCHY_CONTROL: /* 121 */ - return TPM_LONG; - case TPM2_CC_HIERARCHY_CHANGE_AUTH: /* 129 */ - return TPM_LONG; - - case TPM2_CC_GET_CAPABILITY: /* 17A */ - return TPM_MEDIUM; - - case TPM2_CC_NV_READ: /* 14E */ - return TPM_LONG; - - case TPM2_CC_CREATE_PRIMARY: /* 131 */ - return TPM_LONG_LONG; - case TPM2_CC_CREATE: /* 153 */ - return TPM_LONG_LONG; - case TPM2_CC_CREATE_LOADED: /* 191 */ - return TPM_LONG_LONG; - - default: - return TPM_UNDEFINED; - } -} +static const struct { + unsigned long ordinal; + unsigned long duration; +} tpm2_ordinal_duration_map[] = { + {TPM2_CC_STARTUP, 750}, + {TPM2_CC_SELF_TEST, 3000}, + {TPM2_CC_GET_RANDOM, 2000}, + {TPM2_CC_SEQUENCE_UPDATE, 750}, + {TPM2_CC_SEQUENCE_COMPLETE, 750}, + {TPM2_CC_EVENT_SEQUENCE_COMPLETE, 750}, + {TPM2_CC_HASH_SEQUENCE_START, 750}, + {TPM2_CC_VERIFY_SIGNATURE, 30000}, + {TPM2_CC_PCR_EXTEND, 750}, + {TPM2_CC_HIERARCHY_CONTROL, 2000}, + {TPM2_CC_HIERARCHY_CHANGE_AUTH, 2000}, + {TPM2_CC_GET_CAPABILITY, 750}, + {TPM2_CC_NV_READ, 2000}, + {TPM2_CC_CREATE_PRIMARY, 300000}, + {TPM2_CC_CREATE, 300000}, + {TPM2_CC_CREATE_LOADED, 300000}, +}; /** - * tpm2_calc_ordinal_duration() - calculate the maximum command duration - * @chip: TPM chip to use. + * tpm2_calc_ordinal_duration() - Calculate the maximum command duration * @ordinal: TPM command ordinal. * - * The function returns the maximum amount of time the chip could take - * to return the result for a particular ordinal in jiffies. - * - * Return: A maximal duration time for an ordinal in jiffies. + * Returns the maximum amount of time the chip is expected by kernel to + * take in jiffies. */ -unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal) +unsigned long tpm2_calc_ordinal_duration(u32 ordinal) { - unsigned int index; + int i; - index = tpm2_ordinal_duration_index(ordinal); + for (i = 0; i < ARRAY_SIZE(tpm2_ordinal_duration_map); i++) + if (ordinal == tpm2_ordinal_duration_map[i].ordinal) + return msecs_to_jiffies(tpm2_ordinal_duration_map[i].duration); - if (index != TPM_UNDEFINED) - return chip->duration[index]; - else - return msecs_to_jiffies(TPM2_DURATION_DEFAULT); + return msecs_to_jiffies(TPM2_DURATION_DEFAULT); } - struct tpm2_pcr_read_out { __be32 update_cnt; __be32 pcr_selects_cnt; @@ -250,11 +202,15 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, } if (!disable_pcr_integrity) { - tpm_buf_append_name(chip, &buf, pcr_idx, NULL); + rc = tpm_buf_append_name(chip, &buf, pcr_idx, NULL); + if (rc) { + tpm_buf_destroy(&buf); + return rc; + } tpm_buf_append_hmac_session(chip, &buf, 0, NULL, 0); } else { tpm_buf_append_handle(chip, &buf, pcr_idx); - tpm_buf_append_auth(chip, &buf, 0, NULL, 0); + tpm_buf_append_auth(chip, &buf, NULL, 0); } tpm_buf_append_u32(&buf, chip->nr_allocated_banks); @@ -265,8 +221,14 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, chip->allocated_banks[i].digest_size); } - if (!disable_pcr_integrity) - tpm_buf_fill_hmac_session(chip, &buf); + if (!disable_pcr_integrity) { + rc = tpm_buf_fill_hmac_session(chip, &buf); + if (rc) { + tpm_buf_destroy(&buf); + return rc; + } + } + rc = tpm_transmit_cmd(chip, &buf, 0, "attempting extend a PCR value"); if (!disable_pcr_integrity) rc = tpm_buf_check_hmac_response(chip, &buf, rc); @@ -320,11 +282,22 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max) do { tpm_buf_reset(&buf, TPM2_ST_SESSIONS, TPM2_CC_GET_RANDOM); - tpm_buf_append_hmac_session_opt(chip, &buf, TPM2_SA_ENCRYPT - | TPM2_SA_CONTINUE_SESSION, - NULL, 0); + if (tpm2_chip_auth(chip)) { + tpm_buf_append_hmac_session(chip, &buf, + TPM2_SA_ENCRYPT | + TPM2_SA_CONTINUE_SESSION, + NULL, 0); + } else { + offset = buf.handles * 4 + TPM_HEADER_SIZE; + head = (struct tpm_header *)buf.data; + if (tpm_buf_length(&buf) == offset) + head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS); + } tpm_buf_append_u16(&buf, num_bytes); - tpm_buf_fill_hmac_session(chip, &buf); + err = tpm_buf_fill_hmac_session(chip, &buf); + if (err) + goto out; + err = tpm_transmit_cmd(chip, &buf, offsetof(struct tpm2_get_random_out, buffer), @@ -359,7 +332,6 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max) } while (retries-- && total < max); tpm_buf_destroy(&buf); - tpm2_end_auth_session(chip); return total ? total : -EIO; out: @@ -602,11 +574,9 @@ ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip) nr_possible_banks = be32_to_cpup( (__be32 *)&buf.data[TPM_HEADER_SIZE + 5]); - - chip->allocated_banks = kcalloc(nr_possible_banks, - sizeof(*chip->allocated_banks), - GFP_KERNEL); - if (!chip->allocated_banks) { + if (nr_possible_banks > TPM2_MAX_PCR_BANKS) { + pr_err("tpm: out of bank capacity: %u > %u\n", + nr_possible_banks, TPM2_MAX_PCR_BANKS); rc = -ENOMEM; goto out; } diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c index b70165b588ec..a1ae5e1829cb 100644 --- a/drivers/char/tpm/tpm2-sessions.c +++ b/drivers/char/tpm/tpm2-sessions.c @@ -40,11 +40,6 @@ * * These are the usage functions: * - * tpm2_start_auth_session() which allocates the opaque auth structure - * and gets a session from the TPM. This must be called before - * any of the following functions. The session is protected by a - * session_key which is derived from a random salt value - * encrypted to the NULL seed. * tpm2_end_auth_session() kills the session and frees the resources. * Under normal operation this function is done by * tpm_buf_check_hmac_response(), so this is only to be used on @@ -74,8 +69,8 @@ #include <linux/unaligned.h> #include <crypto/kpp.h> #include <crypto/ecdh.h> -#include <crypto/hash.h> -#include <crypto/hmac.h> +#include <crypto/sha2.h> +#include <crypto/utils.h> /* maximum number of names the TPM must remember for authorization */ #define AUTH_MAX_NAMES 3 @@ -131,7 +126,7 @@ struct tpm2_auth { u8 session_key[SHA256_DIGEST_SIZE]; u8 passphrase[SHA256_DIGEST_SIZE]; int passphrase_len; - struct crypto_aes_ctx aes_ctx; + struct aes_enckey aes_key; /* saved session attributes: */ u8 attrs; __be32 ordinal; @@ -149,59 +144,83 @@ struct tpm2_auth { /* * Name Size based on TPM algorithm (assumes no hash bigger than 255) */ -static u8 name_size(const u8 *name) +static int name_size(const u8 *name) { - static u8 size_map[] = { - [TPM_ALG_SHA1] = SHA1_DIGEST_SIZE, - [TPM_ALG_SHA256] = SHA256_DIGEST_SIZE, - [TPM_ALG_SHA384] = SHA384_DIGEST_SIZE, - [TPM_ALG_SHA512] = SHA512_DIGEST_SIZE, - }; - u16 alg = get_unaligned_be16(name); - return size_map[alg] + 2; -} - -static int tpm2_parse_read_public(char *name, struct tpm_buf *buf) -{ - struct tpm_header *head = (struct tpm_header *)buf->data; - off_t offset = TPM_HEADER_SIZE; - u32 tot_len = be32_to_cpu(head->length); - u32 val; - - /* we're starting after the header so adjust the length */ - tot_len -= TPM_HEADER_SIZE; - - /* skip public */ - val = tpm_buf_read_u16(buf, &offset); - if (val > tot_len) - return -EINVAL; - offset += val; - /* name */ - val = tpm_buf_read_u16(buf, &offset); - if (val != name_size(&buf->data[offset])) + u16 hash_alg = get_unaligned_be16(name); + + switch (hash_alg) { + case TPM_ALG_SHA1: + return SHA1_DIGEST_SIZE + 2; + case TPM_ALG_SHA256: + return SHA256_DIGEST_SIZE + 2; + case TPM_ALG_SHA384: + return SHA384_DIGEST_SIZE + 2; + case TPM_ALG_SHA512: + return SHA512_DIGEST_SIZE + 2; + default: + pr_warn("tpm: unsupported name algorithm: 0x%04x\n", hash_alg); return -EINVAL; - memcpy(name, &buf->data[offset], val); - /* forget the rest */ - return 0; + } } -static int tpm2_read_public(struct tpm_chip *chip, u32 handle, char *name) +static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name) { + u32 mso = tpm2_handle_mso(handle); + off_t offset = TPM_HEADER_SIZE; + int rc, name_size_alg; struct tpm_buf buf; - int rc; + + if (mso != TPM2_MSO_PERSISTENT && mso != TPM2_MSO_VOLATILE && + mso != TPM2_MSO_NVRAM) { + memcpy(name, &handle, sizeof(u32)); + return sizeof(u32); + } rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC); if (rc) return rc; tpm_buf_append_u32(&buf, handle); - rc = tpm_transmit_cmd(chip, &buf, 0, "read public"); - if (rc == TPM2_RC_SUCCESS) - rc = tpm2_parse_read_public(name, &buf); - tpm_buf_destroy(&buf); + rc = tpm_transmit_cmd(chip, &buf, 0, "TPM2_ReadPublic"); + if (rc) { + tpm_buf_destroy(&buf); + return tpm_ret_to_err(rc); + } - return rc; + /* Skip TPMT_PUBLIC: */ + offset += tpm_buf_read_u16(&buf, &offset); + + /* + * Ensure space for the length field of TPM2B_NAME and hashAlg field of + * TPMT_HA (the extra four bytes). + */ + if (offset + 4 > tpm_buf_length(&buf)) { + tpm_buf_destroy(&buf); + return -EIO; + } + + rc = tpm_buf_read_u16(&buf, &offset); + name_size_alg = name_size(&buf.data[offset]); + + if (name_size_alg < 0) { + tpm_buf_destroy(&buf); + return name_size_alg; + } + + if (rc != name_size_alg) { + tpm_buf_destroy(&buf); + return -EIO; + } + + if (offset + rc > tpm_buf_length(&buf)) { + tpm_buf_destroy(&buf); + return -EIO; + } + + memcpy(name, &buf.data[offset], rc); + tpm_buf_destroy(&buf); + return name_size_alg; } #endif /* CONFIG_TCG_TPM2_HMAC */ @@ -226,52 +245,79 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, char *name) * As with most tpm_buf operations, success is assumed because failure * will be caused by an incorrect programming model and indicated by a * kernel message. + * + * Ends the authorization session on failure. */ -void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf, - u32 handle, u8 *name) +int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf, + u32 handle, u8 *name) { #ifdef CONFIG_TCG_TPM2_HMAC enum tpm2_mso_type mso = tpm2_handle_mso(handle); struct tpm2_auth *auth; + u16 name_size_alg; int slot; + int ret; #endif if (!tpm2_chip_auth(chip)) { tpm_buf_append_handle(chip, buf, handle); - return; + return 0; } #ifdef CONFIG_TCG_TPM2_HMAC slot = (tpm_buf_length(buf) - TPM_HEADER_SIZE) / 4; if (slot >= AUTH_MAX_NAMES) { - dev_err(&chip->dev, "TPM: too many handles\n"); - return; + dev_err(&chip->dev, "too many handles\n"); + ret = -EIO; + goto err; } auth = chip->auth; - WARN(auth->session != tpm_buf_length(buf), - "name added in wrong place\n"); + if (auth->session != tpm_buf_length(buf)) { + dev_err(&chip->dev, "session state malformed"); + ret = -EIO; + goto err; + } tpm_buf_append_u32(buf, handle); auth->session += 4; if (mso == TPM2_MSO_PERSISTENT || mso == TPM2_MSO_VOLATILE || mso == TPM2_MSO_NVRAM) { - if (!name) - tpm2_read_public(chip, handle, auth->name[slot]); + if (!name) { + ret = tpm2_read_public(chip, handle, auth->name[slot]); + } else { + ret = name_size(name); + } + + if (ret < 0) + goto err; + + name_size_alg = ret; } else { - if (name) - dev_err(&chip->dev, "TPM: Handle does not require name but one is specified\n"); + if (name) { + dev_err(&chip->dev, "handle 0x%08x does not use a name\n", + handle); + ret = -EIO; + goto err; + } } auth->name_h[slot] = handle; if (name) - memcpy(auth->name[slot], name, name_size(name)); + memcpy(auth->name[slot], name, name_size_alg); +#endif + return 0; + +#ifdef CONFIG_TCG_TPM2_HMAC +err: + tpm2_end_auth_session(chip); + return tpm_ret_to_err(ret); #endif } EXPORT_SYMBOL_GPL(tpm_buf_append_name); void tpm_buf_append_auth(struct tpm_chip *chip, struct tpm_buf *buf, - u8 attributes, u8 *passphrase, int passphrase_len) + u8 *passphrase, int passphrase_len) { /* offset tells us where the sessions area begins */ int offset = buf->handles * 4 + TPM_HEADER_SIZE; @@ -332,8 +378,7 @@ void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf, #endif if (!tpm2_chip_auth(chip)) { - tpm_buf_append_auth(chip, buf, attributes, passphrase, - passphrase_len); + tpm_buf_append_auth(chip, buf, passphrase, passphrase_len); return; } @@ -390,51 +435,6 @@ static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy, u32 *handle, u8 *name); /* - * It turns out the crypto hmac(sha256) is hard for us to consume - * because it assumes a fixed key and the TPM seems to change the key - * on every operation, so we weld the hmac init and final functions in - * here to give it the same usage characteristics as a regular hash - */ -static void tpm2_hmac_init(struct sha256_state *sctx, u8 *key, u32 key_len) -{ - u8 pad[SHA256_BLOCK_SIZE]; - int i; - - sha256_init(sctx); - for (i = 0; i < sizeof(pad); i++) { - if (i < key_len) - pad[i] = key[i]; - else - pad[i] = 0; - pad[i] ^= HMAC_IPAD_VALUE; - } - sha256_update(sctx, pad, sizeof(pad)); -} - -static void tpm2_hmac_final(struct sha256_state *sctx, u8 *key, u32 key_len, - u8 *out) -{ - u8 pad[SHA256_BLOCK_SIZE]; - int i; - - for (i = 0; i < sizeof(pad); i++) { - if (i < key_len) - pad[i] = key[i]; - else - pad[i] = 0; - pad[i] ^= HMAC_OPAD_VALUE; - } - - /* collect the final hash; use out as temporary storage */ - sha256_final(sctx, out); - - sha256_init(sctx); - sha256_update(sctx, pad, sizeof(pad)); - sha256_update(sctx, out, SHA256_DIGEST_SIZE); - sha256_final(sctx, out); -} - -/* * assume hash sha256 and nonces u, v of size SHA256_DIGEST_SIZE but * otherwise standard tpm2_KDFa. Note output is in bytes not bits. */ @@ -445,16 +445,16 @@ static void tpm2_KDFa(u8 *key, u32 key_len, const char *label, u8 *u, const __be32 bits = cpu_to_be32(bytes * 8); while (bytes > 0) { - struct sha256_state sctx; + struct hmac_sha256_ctx hctx; __be32 c = cpu_to_be32(counter); - tpm2_hmac_init(&sctx, key, key_len); - sha256_update(&sctx, (u8 *)&c, sizeof(c)); - sha256_update(&sctx, label, strlen(label)+1); - sha256_update(&sctx, u, SHA256_DIGEST_SIZE); - sha256_update(&sctx, v, SHA256_DIGEST_SIZE); - sha256_update(&sctx, (u8 *)&bits, sizeof(bits)); - tpm2_hmac_final(&sctx, key, key_len, out); + hmac_sha256_init_usingrawkey(&hctx, key, key_len); + hmac_sha256_update(&hctx, (u8 *)&c, sizeof(c)); + hmac_sha256_update(&hctx, label, strlen(label) + 1); + hmac_sha256_update(&hctx, u, SHA256_DIGEST_SIZE); + hmac_sha256_update(&hctx, v, SHA256_DIGEST_SIZE); + hmac_sha256_update(&hctx, (u8 *)&bits, sizeof(bits)); + hmac_sha256_final(&hctx, out); bytes -= SHA256_DIGEST_SIZE; counter++; @@ -472,7 +472,7 @@ static void tpm2_KDFa(u8 *key, u32 key_len, const char *label, u8 *u, static void tpm2_KDFe(u8 z[EC_PT_SZ], const char *str, u8 *pt_u, u8 *pt_v, u8 *out) { - struct sha256_state sctx; + struct sha256_ctx sctx; /* * this should be an iterative counter, but because we know * we're only taking 32 bytes for the point using a sha256 @@ -492,15 +492,17 @@ static void tpm2_KDFe(u8 z[EC_PT_SZ], const char *str, u8 *pt_u, u8 *pt_v, sha256_final(&sctx, out); } -static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, - struct tpm2_auth *auth) +static int tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, + struct tpm2_auth *auth) { struct crypto_kpp *kpp; struct kpp_request *req; + DECLARE_CRYPTO_WAIT(wait); struct scatterlist s[2], d[1]; struct ecdh p = {0}; u8 encoded_key[EC_PT_SZ], *x, *y; unsigned int buf_len; + int rc; /* secret is two sized points */ tpm_buf_append_u16(buf, (EC_PT_SZ + 2)*2); @@ -523,14 +525,15 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, kpp = crypto_alloc_kpp("ecdh-nist-p256", CRYPTO_ALG_INTERNAL, 0); if (IS_ERR(kpp)) { dev_err(&chip->dev, "crypto ecdh allocation failed\n"); - return; + return PTR_ERR(kpp); } buf_len = crypto_ecdh_key_len(&p); if (sizeof(encoded_key) < buf_len) { dev_err(&chip->dev, "salt buffer too small needs %d\n", buf_len); - goto out; + rc = -EINVAL; + goto err_free_kpp; } crypto_ecdh_encode_key(encoded_key, buf_len, &p); /* this generates a random private key */ @@ -538,11 +541,17 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, /* salt is now the public point of this private key */ req = kpp_request_alloc(kpp, GFP_KERNEL); - if (!req) - goto out; + if (!req) { + rc = -ENOMEM; + goto err_free_kpp; + } + kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + crypto_req_done, &wait); kpp_request_set_input(req, NULL, 0); kpp_request_set_output(req, s, EC_PT_SZ*2); - crypto_kpp_generate_public_key(req); + rc = crypto_wait_req(crypto_kpp_generate_public_key(req), &wait); + if (rc) + goto err_free_req; /* * we're not done: now we have to compute the shared secret * which is our private key multiplied by the tpm_key public @@ -554,8 +563,9 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, kpp_request_set_input(req, s, EC_PT_SZ*2); sg_init_one(d, auth->salt, EC_PT_SZ); kpp_request_set_output(req, d, EC_PT_SZ); - crypto_kpp_compute_shared_secret(req); - kpp_request_free(req); + rc = crypto_wait_req(crypto_kpp_compute_shared_secret(req), &wait); + if (rc) + goto err_free_req; /* * pass the shared secret through KDFe for salt. Note salt @@ -565,8 +575,16 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, */ tpm2_KDFe(auth->salt, "SECRET", x, chip->null_ec_key_x, auth->salt); - out: + kpp_request_free(req); crypto_free_kpp(kpp); + return 0; + +err_free_req: + kpp_request_free(req); + +err_free_kpp: + crypto_free_kpp(kpp); + return rc; } /** @@ -583,11 +601,9 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, * encryption key and encrypts the first parameter of the command * buffer with it. * - * As with most tpm_buf operations, success is assumed because failure - * will be caused by an incorrect programming model and indicated by a - * kernel message. + * Ends the authorization session on failure. */ -void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf) +int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf) { u32 cc, handles, val; struct tpm2_auth *auth = chip->auth; @@ -597,10 +613,14 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf) u8 *hmac = NULL; u32 attrs; u8 cphash[SHA256_DIGEST_SIZE]; - struct sha256_state sctx; + struct sha256_ctx sctx; + struct hmac_sha256_ctx hctx; + int ret; - if (!auth) - return; + if (!auth) { + ret = -EIO; + goto err; + } /* save the command code in BE format */ auth->ordinal = head->ordinal; @@ -609,9 +629,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf) i = tpm2_find_cc(chip, cc); if (i < 0) { - dev_err(&chip->dev, "Command 0x%x not found in TPM\n", cc); - return; + dev_err(&chip->dev, "command 0x%08x not found\n", cc); + ret = -EIO; + goto err; } + attrs = chip->cc_attrs_tbl[i]; handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0); @@ -625,9 +647,9 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf) u32 handle = tpm_buf_read_u32(buf, &offset_s); if (auth->name_h[i] != handle) { - dev_err(&chip->dev, "TPM: handle %d wrong for name\n", - i); - return; + dev_err(&chip->dev, "invalid handle 0x%08x\n", handle); + ret = -EIO; + goto err; } } /* point offset_s to the start of the sessions */ @@ -658,12 +680,14 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf) offset_s += len; } if (offset_s != offset_p) { - dev_err(&chip->dev, "TPM session length is incorrect\n"); - return; + dev_err(&chip->dev, "session length is incorrect\n"); + ret = -EIO; + goto err; } if (!hmac) { - dev_err(&chip->dev, "TPM could not find HMAC session\n"); - return; + dev_err(&chip->dev, "could not find HMAC session\n"); + ret = -EIO; + goto err; } /* encrypt before HMAC */ @@ -677,8 +701,8 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf) auth->scratch); len = tpm_buf_read_u16(buf, &offset_p); - aes_expandkey(&auth->aes_ctx, auth->scratch, AES_KEY_BYTES); - aescfb_encrypt(&auth->aes_ctx, &buf->data[offset_p], + aes_prepareenckey(&auth->aes_key, auth->scratch, AES_KEY_BYTES); + aescfb_encrypt(&auth->aes_key, &buf->data[offset_p], &buf->data[offset_p], len, auth->scratch + AES_KEY_BYTES); /* reset p to beginning of parameters for HMAC */ @@ -695,8 +719,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf) if (mso == TPM2_MSO_PERSISTENT || mso == TPM2_MSO_VOLATILE || mso == TPM2_MSO_NVRAM) { - sha256_update(&sctx, auth->name[i], - name_size(auth->name[i])); + ret = name_size(auth->name[i]); + if (ret < 0) + goto err; + + sha256_update(&sctx, auth->name[i], ret); } else { __be32 h = cpu_to_be32(auth->name_h[i]); @@ -709,14 +736,19 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf) sha256_final(&sctx, cphash); /* now calculate the hmac */ - tpm2_hmac_init(&sctx, auth->session_key, sizeof(auth->session_key) - + auth->passphrase_len); - sha256_update(&sctx, cphash, sizeof(cphash)); - sha256_update(&sctx, auth->our_nonce, sizeof(auth->our_nonce)); - sha256_update(&sctx, auth->tpm_nonce, sizeof(auth->tpm_nonce)); - sha256_update(&sctx, &auth->attrs, 1); - tpm2_hmac_final(&sctx, auth->session_key, sizeof(auth->session_key) - + auth->passphrase_len, hmac); + hmac_sha256_init_usingrawkey(&hctx, auth->session_key, + sizeof(auth->session_key) + + auth->passphrase_len); + hmac_sha256_update(&hctx, cphash, sizeof(cphash)); + hmac_sha256_update(&hctx, auth->our_nonce, sizeof(auth->our_nonce)); + hmac_sha256_update(&hctx, auth->tpm_nonce, sizeof(auth->tpm_nonce)); + hmac_sha256_update(&hctx, &auth->attrs, 1); + hmac_sha256_final(&hctx, hmac); + return 0; + +err: + tpm2_end_auth_session(chip); + return ret; } EXPORT_SYMBOL(tpm_buf_fill_hmac_session); @@ -755,7 +787,8 @@ int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf, off_t offset_s, offset_p; u8 rphash[SHA256_DIGEST_SIZE]; u32 attrs, cc; - struct sha256_state sctx; + struct sha256_ctx sctx; + struct hmac_sha256_ctx hctx; u16 tag = be16_to_cpu(head->tag); int parm_len, len, i, handles; @@ -825,21 +858,20 @@ int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf, sha256_final(&sctx, rphash); /* now calculate the hmac */ - tpm2_hmac_init(&sctx, auth->session_key, sizeof(auth->session_key) - + auth->passphrase_len); - sha256_update(&sctx, rphash, sizeof(rphash)); - sha256_update(&sctx, auth->tpm_nonce, sizeof(auth->tpm_nonce)); - sha256_update(&sctx, auth->our_nonce, sizeof(auth->our_nonce)); - sha256_update(&sctx, &auth->attrs, 1); + hmac_sha256_init_usingrawkey(&hctx, auth->session_key, + sizeof(auth->session_key) + + auth->passphrase_len); + hmac_sha256_update(&hctx, rphash, sizeof(rphash)); + hmac_sha256_update(&hctx, auth->tpm_nonce, sizeof(auth->tpm_nonce)); + hmac_sha256_update(&hctx, auth->our_nonce, sizeof(auth->our_nonce)); + hmac_sha256_update(&hctx, &auth->attrs, 1); /* we're done with the rphash, so put our idea of the hmac there */ - tpm2_hmac_final(&sctx, auth->session_key, sizeof(auth->session_key) - + auth->passphrase_len, rphash); - if (memcmp(rphash, &buf->data[offset_s], SHA256_DIGEST_SIZE) == 0) { - rc = 0; - } else { + hmac_sha256_final(&hctx, rphash); + if (crypto_memneq(rphash, &buf->data[offset_s], SHA256_DIGEST_SIZE)) { dev_err(&chip->dev, "TPM: HMAC check failed\n"); goto out; } + rc = 0; /* now do response decryption */ if (auth->attrs & TPM2_SA_ENCRYPT) { @@ -850,8 +882,8 @@ int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf, auth->scratch); len = tpm_buf_read_u16(buf, &offset_p); - aes_expandkey(&auth->aes_ctx, auth->scratch, AES_KEY_BYTES); - aescfb_decrypt(&auth->aes_ctx, &buf->data[offset_p], + aes_prepareenckey(&auth->aes_key, auth->scratch, AES_KEY_BYTES); + aescfb_decrypt(&auth->aes_key, &buf->data[offset_p], &buf->data[offset_p], len, auth->scratch + AES_KEY_BYTES); } @@ -963,16 +995,13 @@ err: } /** - * tpm2_start_auth_session() - create a HMAC authentication session with the TPM - * @chip: the TPM chip structure to create the session with + * tpm2_start_auth_session() - Create an a HMAC authentication session + * @chip: A TPM chip * - * This function loads the NULL seed from its saved context and starts - * an authentication session on the null seed, fills in the - * @chip->auth structure to contain all the session details necessary - * for performing the HMAC, encrypt and decrypt operations and - * returns. The NULL seed is flushed before this function returns. + * Loads the ephemeral key (null seed), and starts an HMAC authenticated + * session. The null seed is flushed before the return. * - * Return: zero on success or actual error encountered. + * Returns zero on success, or a POSIX error code. */ int tpm2_start_auth_session(struct tpm_chip *chip) { @@ -982,11 +1011,11 @@ int tpm2_start_auth_session(struct tpm_chip *chip) int rc; if (chip->auth) { - dev_warn_once(&chip->dev, "auth session is active\n"); + dev_dbg_once(&chip->dev, "auth session is active\n"); return 0; } - auth = kzalloc(sizeof(*auth), GFP_KERNEL); + auth = kzalloc_obj(*auth); if (!auth) return -ENOMEM; @@ -1010,7 +1039,12 @@ int tpm2_start_auth_session(struct tpm_chip *chip) tpm_buf_append(&buf, auth->our_nonce, sizeof(auth->our_nonce)); /* append encrypted salt and squirrel away unencrypted in auth */ - tpm_buf_append_salt(&buf, chip, auth); + rc = tpm_buf_append_salt(&buf, chip, auth); + if (rc) { + tpm2_flush_context(chip, null_key); + tpm_buf_destroy(&buf); + goto out; + } /* session type (HMAC, audit or policy) */ tpm_buf_append_u8(&buf, TPM2_SE_HMAC); @@ -1024,7 +1058,7 @@ int tpm2_start_auth_session(struct tpm_chip *chip) /* hash algorithm for session */ tpm_buf_append_u16(&buf, TPM_ALG_SHA256); - rc = tpm_transmit_cmd(chip, &buf, 0, "start auth session"); + rc = tpm_ret_to_err(tpm_transmit_cmd(chip, &buf, 0, "StartAuthSession")); tpm2_flush_context(chip, null_key); if (rc == TPM2_RC_SUCCESS) diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c index 54a0360a3c95..f25faf468bba 100644 --- a/drivers/char/tpm/tpm_atmel.c +++ b/drivers/char/tpm/tpm_atmel.c @@ -148,7 +148,8 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count) return size; } -static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count) +static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t count) { struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev); int i; diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index ea085b14ab7c..ceb4100ba400 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -15,10 +15,12 @@ #include <linux/highmem.h> #include <linux/rculist.h> #include <linux/module.h> +#include <linux/platform_device.h> #include <linux/pm_runtime.h> #ifdef CONFIG_ARM64 #include <linux/arm-smccc.h> #endif +#include "tpm_crb_ffa.h" #include "tpm.h" #define ACPI_SIG_TPM2 "TPM2" @@ -100,6 +102,8 @@ struct crb_priv { u32 smc_func_id; u32 __iomem *pluton_start_addr; u32 __iomem *pluton_reply_addr; + u8 ffa_flags; + u8 ffa_attributes; }; struct tpm2_crb_smc { @@ -110,11 +114,29 @@ struct tpm2_crb_smc { u32 smc_func_id; }; +/* CRB over FFA start method parameters in TCG2 ACPI table */ +struct tpm2_crb_ffa { + u8 flags; + u8 attributes; + u16 partition_id; + u8 reserved[8]; +}; + struct tpm2_crb_pluton { u64 start_addr; u64 reply_addr; }; +/* + * Returns true if the start method supports idle. + */ +static inline bool tpm_crb_has_idle(u32 start_method) +{ + return !(start_method == ACPI_TPM2_START_METHOD || + start_method == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD || + start_method == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC); +} + static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value, unsigned long timeout) { @@ -158,6 +180,7 @@ static int crb_try_pluton_doorbell(struct crb_priv *priv, bool wait_for_complete * * @dev: crb device * @priv: crb private data + * @loc: locality * * Write CRB_CTRL_REQ_GO_IDLE to TPM_CRB_CTRL_REQ * The device should respond within TIMEOUT_C by clearing the bit. @@ -169,17 +192,21 @@ static int crb_try_pluton_doorbell(struct crb_priv *priv, bool wait_for_complete * * Return: 0 always */ -static int __crb_go_idle(struct device *dev, struct crb_priv *priv) +static int __crb_go_idle(struct device *dev, struct crb_priv *priv, int loc) { int rc; - if ((priv->sm == ACPI_TPM2_START_METHOD) || - (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) || - (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC)) + if (!tpm_crb_has_idle(priv->sm)) return 0; iowrite32(CRB_CTRL_REQ_GO_IDLE, &priv->regs_t->ctrl_req); + if (priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA) { + rc = tpm_crb_ffa_start(CRB_FFA_START_TYPE_COMMAND, loc); + if (rc) + return rc; + } + rc = crb_try_pluton_doorbell(priv, true); if (rc) return rc; @@ -200,7 +227,7 @@ static int crb_go_idle(struct tpm_chip *chip) struct device *dev = &chip->dev; struct crb_priv *priv = dev_get_drvdata(dev); - return __crb_go_idle(dev, priv); + return __crb_go_idle(dev, priv, chip->locality); } /** @@ -208,6 +235,7 @@ static int crb_go_idle(struct tpm_chip *chip) * * @dev: crb device * @priv: crb private data + * @loc: locality * * Write CRB_CTRL_REQ_CMD_READY to TPM_CRB_CTRL_REQ * and poll till the device acknowledge it by clearing the bit. @@ -218,17 +246,21 @@ static int crb_go_idle(struct tpm_chip *chip) * * Return: 0 on success -ETIME on timeout; */ -static int __crb_cmd_ready(struct device *dev, struct crb_priv *priv) +static int __crb_cmd_ready(struct device *dev, struct crb_priv *priv, int loc) { int rc; - if ((priv->sm == ACPI_TPM2_START_METHOD) || - (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) || - (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC)) + if (!tpm_crb_has_idle(priv->sm)) return 0; iowrite32(CRB_CTRL_REQ_CMD_READY, &priv->regs_t->ctrl_req); + if (priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA) { + rc = tpm_crb_ffa_start(CRB_FFA_START_TYPE_COMMAND, loc); + if (rc) + return rc; + } + rc = crb_try_pluton_doorbell(priv, true); if (rc) return rc; @@ -249,19 +281,26 @@ static int crb_cmd_ready(struct tpm_chip *chip) struct device *dev = &chip->dev; struct crb_priv *priv = dev_get_drvdata(dev); - return __crb_cmd_ready(dev, priv); + return __crb_cmd_ready(dev, priv, chip->locality); } static int __crb_request_locality(struct device *dev, struct crb_priv *priv, int loc) { - u32 value = CRB_LOC_STATE_LOC_ASSIGNED | - CRB_LOC_STATE_TPM_REG_VALID_STS; + u32 value = CRB_LOC_STATE_LOC_ASSIGNED | CRB_LOC_STATE_TPM_REG_VALID_STS; + int rc; if (!priv->regs_h) return 0; iowrite32(CRB_LOC_CTRL_REQUEST_ACCESS, &priv->regs_h->loc_ctrl); + + if (priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA) { + rc = tpm_crb_ffa_start(CRB_FFA_START_TYPE_LOCALITY_REQUEST, loc); + if (rc) + return rc; + } + if (!crb_wait_for_reg_32(&priv->regs_h->loc_state, value, value, TPM2_TIMEOUT_C)) { dev_warn(dev, "TPM_LOC_STATE_x.requestAccess timed out\n"); @@ -281,14 +320,21 @@ static int crb_request_locality(struct tpm_chip *chip, int loc) static int __crb_relinquish_locality(struct device *dev, struct crb_priv *priv, int loc) { - u32 mask = CRB_LOC_STATE_LOC_ASSIGNED | - CRB_LOC_STATE_TPM_REG_VALID_STS; + u32 mask = CRB_LOC_STATE_LOC_ASSIGNED | CRB_LOC_STATE_TPM_REG_VALID_STS; u32 value = CRB_LOC_STATE_TPM_REG_VALID_STS; + int rc; if (!priv->regs_h) return 0; iowrite32(CRB_LOC_CTRL_RELINQUISH, &priv->regs_h->loc_ctrl); + + if (priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA) { + rc = tpm_crb_ffa_start(CRB_FFA_START_TYPE_LOCALITY_REQUEST, loc); + if (rc) + return rc; + } + if (!crb_wait_for_reg_32(&priv->regs_h->loc_state, mask, value, TPM2_TIMEOUT_C)) { dev_warn(dev, "TPM_LOC_STATE_x.Relinquish timed out\n"); @@ -369,7 +415,7 @@ static int crb_do_acpi_start(struct tpm_chip *chip) #ifdef CONFIG_ARM64 /* * This is a TPM Command Response Buffer start method that invokes a - * Secure Monitor Call to requrest the firmware to execute or cancel + * Secure Monitor Call to request the firmware to execute or cancel * a TPM 2.0 command. */ static int tpm_crb_smc_start(struct device *dev, unsigned long func_id) @@ -394,7 +440,7 @@ static int tpm_crb_smc_start(struct device *dev, unsigned long func_id) } #endif -static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len) +static int crb_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, size_t len) { struct crb_priv *priv = dev_get_drvdata(&chip->dev); int rc = 0; @@ -412,7 +458,7 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len) /* Seems to be necessary for every command */ if (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON) - __crb_cmd_ready(&chip->dev, priv); + __crb_cmd_ready(&chip->dev, priv, chip->locality); memcpy_toio(priv->cmd, buf, len); @@ -423,13 +469,13 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len) * report only ACPI start but in practice seems to require both * CRB start, hence invoking CRB start method if hid == MSFT0101. */ - if ((priv->sm == ACPI_TPM2_COMMAND_BUFFER) || - (priv->sm == ACPI_TPM2_MEMORY_MAPPED) || - (!strcmp(priv->hid, "MSFT0101"))) + if (priv->sm == ACPI_TPM2_COMMAND_BUFFER || + priv->sm == ACPI_TPM2_MEMORY_MAPPED || + !strcmp(priv->hid, "MSFT0101")) iowrite32(CRB_START_INVOKE, &priv->regs_t->ctrl_start); - if ((priv->sm == ACPI_TPM2_START_METHOD) || - (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD)) + if (priv->sm == ACPI_TPM2_START_METHOD || + priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) rc = crb_do_acpi_start(chip); if (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC) { @@ -437,6 +483,11 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len) rc = tpm_crb_smc_start(&chip->dev, priv->smc_func_id); } + if (priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA) { + iowrite32(CRB_START_INVOKE, &priv->regs_t->ctrl_start); + rc = tpm_crb_ffa_start(CRB_FFA_START_TYPE_COMMAND, chip->locality); + } + if (rc) return rc; @@ -446,13 +497,20 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len) static void crb_cancel(struct tpm_chip *chip) { struct crb_priv *priv = dev_get_drvdata(&chip->dev); + int rc; iowrite32(CRB_CANCEL_INVOKE, &priv->regs_t->ctrl_cancel); - if (((priv->sm == ACPI_TPM2_START_METHOD) || - (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD)) && + if ((priv->sm == ACPI_TPM2_START_METHOD || + priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) && crb_do_acpi_start(chip)) dev_err(&chip->dev, "ACPI Start failed\n"); + + if (priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA) { + rc = tpm_crb_ffa_start(CRB_FFA_START_TYPE_COMMAND, chip->locality); + if (rc) + dev_err(&chip->dev, "FF-A Start failed\n"); + } } static bool crb_req_canceled(struct tpm_chip *chip, u8 status) @@ -545,13 +603,13 @@ static u64 crb_fixup_cmd_size(struct device *dev, struct resource *io_res, return io_res->end - start + 1; } -static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, +static int crb_map_io(struct device *dev, struct crb_priv *priv, struct acpi_table_tpm2 *buf) { + struct acpi_device *device = ACPI_COMPANION(dev); struct list_head acpi_resource_list; struct resource iores_array[TPM_CRB_MAX_RESOURCES + 1] = { {0} }; void __iomem *iobase_array[TPM_CRB_MAX_RESOURCES] = {NULL}; - struct device *dev = &device->dev; struct resource *iores; void __iomem **iobase_ptr; int i; @@ -609,8 +667,9 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, * the control area, as one nice sane region except for some older * stuff that puts the control area outside the ACPI IO region. */ - if ((priv->sm == ACPI_TPM2_COMMAND_BUFFER) || - (priv->sm == ACPI_TPM2_MEMORY_MAPPED)) { + if (priv->sm == ACPI_TPM2_COMMAND_BUFFER || + priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA || + priv->sm == ACPI_TPM2_MEMORY_MAPPED) { if (iores && buf->control_address == iores->start + sizeof(*priv->regs_h)) @@ -627,7 +686,7 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, * PTT HW bug w/a: wake up the device to access * possibly not retained registers. */ - ret = __crb_cmd_ready(dev, priv); + ret = __crb_cmd_ready(dev, priv, 0); if (ret) goto out_relinquish_locality; @@ -699,7 +758,7 @@ out: if (!ret) priv->cmd_size = cmd_size; - __crb_go_idle(dev, priv); + __crb_go_idle(dev, priv, 0); out_relinquish_locality: @@ -724,18 +783,24 @@ static int crb_map_pluton(struct device *dev, struct crb_priv *priv, return 0; } -static int crb_acpi_add(struct acpi_device *device) +static int crb_acpi_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct acpi_table_tpm2 *buf; + struct acpi_device *device; struct crb_priv *priv; struct tpm_chip *chip; - struct device *dev = &device->dev; struct tpm2_crb_smc *crb_smc; + struct tpm2_crb_ffa *crb_ffa; struct tpm2_crb_pluton *crb_pluton; acpi_status status; u32 sm; int rc; + device = ACPI_COMPANION(dev); + if (!device) + return -ENODEV; + status = acpi_get_table(ACPI_SIG_TPM2, 1, (struct acpi_table_header **) &buf); if (ACPI_FAILURE(status) || buf->header.length < sizeof(*buf)) { @@ -769,6 +834,27 @@ static int crb_acpi_add(struct acpi_device *device) priv->smc_func_id = crb_smc->smc_func_id; } + if (sm == ACPI_TPM2_CRB_WITH_ARM_FFA) { + if (buf->header.length < (sizeof(*buf) + sizeof(*crb_ffa))) { + dev_err(dev, + FW_BUG "TPM2 ACPI table has wrong size %u for start method type %d\n", + buf->header.length, + ACPI_TPM2_CRB_WITH_ARM_FFA); + rc = -EINVAL; + goto out; + } + crb_ffa = ACPI_ADD_PTR(struct tpm2_crb_ffa, buf, sizeof(*buf)); + priv->ffa_flags = crb_ffa->flags; + priv->ffa_attributes = crb_ffa->attributes; + rc = tpm_crb_ffa_init(); + if (rc) { + /* If FF-A driver is not available yet, request probe retry */ + if (rc == -ENOENT) + rc = -EPROBE_DEFER; + goto out; + } + } + if (sm == ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON) { if (buf->header.length < (sizeof(*buf) + sizeof(*crb_pluton))) { dev_err(dev, @@ -787,7 +873,7 @@ static int crb_acpi_add(struct acpi_device *device) priv->sm = sm; priv->hid = acpi_device_hid(device); - rc = crb_map_io(device, priv, buf); + rc = crb_map_io(dev, priv, buf); if (rc) goto out; @@ -821,12 +907,9 @@ out: return rc; } -static void crb_acpi_remove(struct acpi_device *device) +static void crb_acpi_remove(struct platform_device *pdev) { - struct device *dev = &device->dev; - struct tpm_chip *chip = dev_get_drvdata(dev); - - tpm_chip_unregister(chip); + tpm_chip_unregister(platform_get_drvdata(pdev)); } static const struct dev_pm_ops crb_pm = { @@ -839,19 +922,17 @@ static const struct acpi_device_id crb_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, crb_device_ids); -static struct acpi_driver crb_acpi_driver = { - .name = "tpm_crb", - .ids = crb_device_ids, - .ops = { - .add = crb_acpi_add, - .remove = crb_acpi_remove, - }, - .drv = { +static struct platform_driver crb_acpi_driver = { + .probe = crb_acpi_probe, + .remove = crb_acpi_remove, + .driver = { + .name = "tpm_crb_acpi", + .acpi_match_table = crb_device_ids, .pm = &crb_pm, }, }; -module_acpi_driver(crb_acpi_driver); +module_platform_driver(crb_acpi_driver); MODULE_AUTHOR("Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>"); MODULE_DESCRIPTION("TPM2 Driver"); MODULE_VERSION("0.1"); diff --git a/drivers/char/tpm/tpm_crb_ffa.c b/drivers/char/tpm/tpm_crb_ffa.c new file mode 100644 index 000000000000..025c4d4b17ca --- /dev/null +++ b/drivers/char/tpm/tpm_crb_ffa.c @@ -0,0 +1,402 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2024 Arm Ltd. + * + * This device driver implements the TPM CRB start method + * as defined in the TPM Service Command Response Buffer + * Interface Over FF-A (DEN0138). + */ + +#define pr_fmt(fmt) "CRB_FFA: " fmt + +#include <linux/arm_ffa.h> +#include <linux/delay.h> +#include <linux/moduleparam.h> +#include "tpm_crb_ffa.h" + +static unsigned int busy_timeout_ms = 2000; + +module_param(busy_timeout_ms, uint, 0644); +MODULE_PARM_DESC(busy_timeout_ms, + "Maximum time in ms to retry before giving up on busy"); + +/* TPM service function status codes */ +#define CRB_FFA_OK 0x05000001 +#define CRB_FFA_OK_RESULTS_RETURNED 0x05000002 +#define CRB_FFA_NOFUNC 0x8e000001 +#define CRB_FFA_NOTSUP 0x8e000002 +#define CRB_FFA_INVARG 0x8e000005 +#define CRB_FFA_INV_CRB_CTRL_DATA 0x8e000006 +#define CRB_FFA_ALREADY 0x8e000009 +#define CRB_FFA_DENIED 0x8e00000a +#define CRB_FFA_NOMEM 0x8e00000b + +#define CRB_FFA_VERSION_MAJOR 1 +#define CRB_FFA_VERSION_MINOR 0 + +/* version encoding */ +#define CRB_FFA_MAJOR_VERSION_MASK GENMASK(30, 16) +#define CRB_FFA_MINOR_VERSION_MASK GENMASK(15, 0) +#define CRB_FFA_MAJOR_VERSION(x) ((u16)(FIELD_GET(CRB_FFA_MAJOR_VERSION_MASK, (x)))) +#define CRB_FFA_MINOR_VERSION(x) ((u16)(FIELD_GET(CRB_FFA_MINOR_VERSION_MASK, (x)))) + +/* + * Normal world sends requests with FFA_MSG_SEND_DIRECT_REQ and + * responses are returned with FFA_MSG_SEND_DIRECT_RESP for normal + * messages. + * + * All requests with FFA_MSG_SEND_DIRECT_REQ and FFA_MSG_SEND_DIRECT_RESP + * are using the AArch32 or AArch64 SMC calling convention with register usage + * as defined in FF-A specification: + * w0: Function ID + * -for 32-bit: 0x8400006F or 0x84000070 + * -for 64-bit: 0xC400006F or 0xC4000070 + * w1: Source/Destination IDs + * w2: Reserved (MBZ) + * w3-w7: Implementation defined, free to be used below + */ + +/* + * Returns the version of the interface that is available + * Call register usage: + * w3: Not used (MBZ) + * w4: TPM service function ID, CRB_FFA_GET_INTERFACE_VERSION + * w5-w7: Reserved (MBZ) + * + * Return register usage: + * w3: Not used (MBZ) + * w4: TPM service function status + * w5: TPM service interface version + * Bits[31:16]: major version + * Bits[15:0]: minor version + * w6-w7: Reserved (MBZ) + * + * Possible function status codes in register w4: + * CRB_FFA_OK_RESULTS_RETURNED: The version of the interface has been + * returned. + */ +#define CRB_FFA_GET_INTERFACE_VERSION 0x0f000001 + +/* + * Notifies the TPM service that a TPM command or TPM locality request is + * ready to be processed, and allows the TPM service to process it. + * Call register usage: + * w3: Not used (MBZ) + * w4: TPM service function ID, CRB_FFA_START + * w5: Start function qualifier + * Bits[31:8] (MBZ) + * Bits[7:0] + * 0: Notifies TPM that a command is ready to be processed + * 1: Notifies TPM that a locality request is ready to be processed + * w6: TPM locality, one of 0..4 + * -If the start function qualifier is 0, identifies the locality + * from where the command originated. + * -If the start function qualifier is 1, identifies the locality + * of the locality request + * w6-w7: Reserved (MBZ) + * + * Return register usage: + * w3: Not used (MBZ) + * w4: TPM service function status + * w5-w7: Reserved (MBZ) + * + * Possible function status codes in register w4: + * CRB_FFA_OK: the TPM service has been notified successfully + * CRB_FFA_INVARG: one or more arguments are not valid + * CRB_FFA_INV_CRB_CTRL_DATA: CRB control data or locality control + * data at the given TPM locality is not valid + * CRB_FFA_DENIED: the TPM has previously disabled locality requests and + * command processing at the given locality + */ +#define CRB_FFA_START 0x0f000201 + +struct tpm_crb_ffa { + struct ffa_device *ffa_dev; + u16 major_version; + u16 minor_version; + /* lock to protect sending of FF-A messages: */ + struct mutex msg_data_lock; + union { + struct ffa_send_direct_data direct_msg_data; + struct ffa_send_direct_data2 direct_msg_data2; + }; +}; + +static struct tpm_crb_ffa *tpm_crb_ffa; +static struct ffa_driver tpm_crb_ffa_driver; + +static int tpm_crb_ffa_to_linux_errno(int errno) +{ + int rc; + + switch (errno) { + case CRB_FFA_OK: + rc = 0; + break; + case CRB_FFA_OK_RESULTS_RETURNED: + rc = 0; + break; + case CRB_FFA_NOFUNC: + rc = -ENOENT; + break; + case CRB_FFA_NOTSUP: + rc = -EPERM; + break; + case CRB_FFA_INVARG: + rc = -EINVAL; + break; + case CRB_FFA_INV_CRB_CTRL_DATA: + rc = -ENOEXEC; + break; + case CRB_FFA_ALREADY: + rc = -EEXIST; + break; + case CRB_FFA_DENIED: + rc = -EACCES; + break; + case CRB_FFA_NOMEM: + rc = -ENOMEM; + break; + default: + rc = -EINVAL; + } + + return rc; +} + +/** + * tpm_crb_ffa_init - called by the CRB driver to do any needed initialization + * + * This function is called by the tpm_crb driver during the tpm_crb + * driver's initialization. If the tpm_crb_ffa has not been probed + * yet, returns -ENOENT in order to force a retry. If th ffa_crb + * driver had been probed but failed with an error, returns -ENODEV + * in order to prevent further retries. + * + * Return: 0 on success, negative error code on failure. + */ +int tpm_crb_ffa_init(void) +{ + if (!tpm_crb_ffa) + return -ENOENT; + + if (IS_ERR_VALUE(tpm_crb_ffa)) + return -ENODEV; + + return 0; +} +EXPORT_SYMBOL_GPL(tpm_crb_ffa_init); + +static int __tpm_crb_ffa_try_send_receive(unsigned long func_id, + unsigned long a0, unsigned long a1, + unsigned long a2) +{ + const struct ffa_msg_ops *msg_ops; + int ret; + + msg_ops = tpm_crb_ffa->ffa_dev->ops->msg_ops; + + if (ffa_partition_supports_direct_req2_recv(tpm_crb_ffa->ffa_dev)) { + tpm_crb_ffa->direct_msg_data2 = (struct ffa_send_direct_data2){ + .data = { func_id, a0, a1, a2 }, + }; + + ret = msg_ops->sync_send_receive2(tpm_crb_ffa->ffa_dev, + &tpm_crb_ffa->direct_msg_data2); + if (!ret) + ret = tpm_crb_ffa_to_linux_errno(tpm_crb_ffa->direct_msg_data2.data[0]); + } else { + tpm_crb_ffa->direct_msg_data = (struct ffa_send_direct_data){ + .data1 = func_id, + .data2 = a0, + .data3 = a1, + .data4 = a2, + }; + + ret = msg_ops->sync_send_receive(tpm_crb_ffa->ffa_dev, + &tpm_crb_ffa->direct_msg_data); + if (!ret) + ret = tpm_crb_ffa_to_linux_errno(tpm_crb_ffa->direct_msg_data.data1); + } + + return ret; +} + +static int __tpm_crb_ffa_send_receive(unsigned long func_id, unsigned long a0, + unsigned long a1, unsigned long a2) +{ + ktime_t start, stop; + int ret; + + if (!tpm_crb_ffa) + return -ENOENT; + + start = ktime_get(); + stop = ktime_add(start, ms_to_ktime(busy_timeout_ms)); + + for (;;) { + ret = __tpm_crb_ffa_try_send_receive(func_id, a0, a1, a2); + if (ret != -EBUSY) + break; + + usleep_range(50, 100); + if (ktime_after(ktime_get(), stop)) { + dev_warn(&tpm_crb_ffa->ffa_dev->dev, + "Busy retry timed out\n"); + break; + } + } + + return ret; +} + +/** + * tpm_crb_ffa_get_interface_version() - gets the ABI version of the TPM service + * @major: Pointer to caller-allocated buffer to hold the major version + * number the ABI + * @minor: Pointer to caller-allocated buffer to hold the minor version + * number the ABI + * + * Returns the major and minor version of the ABI of the FF-A based TPM. + * Allows the caller to evaluate its compatibility with the version of + * the ABI. + * + * Return: 0 on success, negative error code on failure. + */ +static int tpm_crb_ffa_get_interface_version(u16 *major, u16 *minor) +{ + int rc; + + if (!tpm_crb_ffa) + return -ENOENT; + + if (IS_ERR_VALUE(tpm_crb_ffa)) + return -ENODEV; + + if (!major || !minor) + return -EINVAL; + + guard(mutex)(&tpm_crb_ffa->msg_data_lock); + + rc = __tpm_crb_ffa_send_receive(CRB_FFA_GET_INTERFACE_VERSION, 0x00, 0x00, 0x00); + if (!rc) { + if (ffa_partition_supports_direct_req2_recv(tpm_crb_ffa->ffa_dev)) { + *major = CRB_FFA_MAJOR_VERSION(tpm_crb_ffa->direct_msg_data2.data[1]); + *minor = CRB_FFA_MINOR_VERSION(tpm_crb_ffa->direct_msg_data2.data[1]); + } else { + *major = CRB_FFA_MAJOR_VERSION(tpm_crb_ffa->direct_msg_data.data2); + *minor = CRB_FFA_MINOR_VERSION(tpm_crb_ffa->direct_msg_data.data2); + } + } + + return rc; +} + +/** + * tpm_crb_ffa_start() - signals the TPM that a field has changed in the CRB + * @request_type: Identifies whether the change to the CRB is in the command + * fields or locality fields. + * @locality: Specifies the locality number. + * + * Used by the CRB driver + * that might be useful to those using or modifying it. Begins with + * empty comment line, and may include additional embedded empty + * comment lines. + * + * Return: 0 on success, negative error code on failure. + */ +int tpm_crb_ffa_start(int request_type, int locality) +{ + if (!tpm_crb_ffa) + return -ENOENT; + + if (IS_ERR_VALUE(tpm_crb_ffa)) + return -ENODEV; + + guard(mutex)(&tpm_crb_ffa->msg_data_lock); + + return __tpm_crb_ffa_send_receive(CRB_FFA_START, request_type, locality, 0x00); +} +EXPORT_SYMBOL_GPL(tpm_crb_ffa_start); + +static int tpm_crb_ffa_probe(struct ffa_device *ffa_dev) +{ + struct tpm_crb_ffa *p; + int rc; + + /* only one instance of a TPM partition is supported */ + if (tpm_crb_ffa && !IS_ERR_VALUE(tpm_crb_ffa)) + return -EEXIST; + + tpm_crb_ffa = ERR_PTR(-ENODEV); // set tpm_crb_ffa so we can detect probe failure + + if (!ffa_partition_supports_direct_recv(ffa_dev) && + !ffa_partition_supports_direct_req2_recv(ffa_dev)) { + dev_warn(&ffa_dev->dev, "partition doesn't support direct message receive.\n"); + return -EINVAL; + } + + p = kzalloc_obj(*tpm_crb_ffa); + if (!p) + return -ENOMEM; + tpm_crb_ffa = p; + + mutex_init(&tpm_crb_ffa->msg_data_lock); + tpm_crb_ffa->ffa_dev = ffa_dev; + ffa_dev_set_drvdata(ffa_dev, tpm_crb_ffa); + + /* if TPM is aarch32 use 32-bit SMCs */ + if (!ffa_partition_check_property(ffa_dev, FFA_PARTITION_AARCH64_EXEC)) + ffa_dev->ops->msg_ops->mode_32bit_set(ffa_dev); + + /* verify compatibility of TPM service version number */ + rc = tpm_crb_ffa_get_interface_version(&tpm_crb_ffa->major_version, + &tpm_crb_ffa->minor_version); + if (rc) { + dev_err(&ffa_dev->dev, "failed to get crb interface version. rc:%d\n", rc); + goto out; + } + + dev_info(&ffa_dev->dev, "ABI version %u.%u\n", tpm_crb_ffa->major_version, + tpm_crb_ffa->minor_version); + + if (tpm_crb_ffa->major_version != CRB_FFA_VERSION_MAJOR || + (tpm_crb_ffa->minor_version > 0 && + tpm_crb_ffa->minor_version < CRB_FFA_VERSION_MINOR)) { + dev_warn(&ffa_dev->dev, "Incompatible ABI version\n"); + goto out; + } + + return 0; + +out: + kfree(tpm_crb_ffa); + tpm_crb_ffa = ERR_PTR(-ENODEV); + return -EINVAL; +} + +static void tpm_crb_ffa_remove(struct ffa_device *ffa_dev) +{ + kfree(tpm_crb_ffa); + tpm_crb_ffa = NULL; +} + +static const struct ffa_device_id tpm_crb_ffa_device_id[] = { + /* 17b862a4-1806-4faf-86b3-089a58353861 */ + { UUID_INIT(0x17b862a4, 0x1806, 0x4faf, + 0x86, 0xb3, 0x08, 0x9a, 0x58, 0x35, 0x38, 0x61) }, + {} +}; + +static struct ffa_driver tpm_crb_ffa_driver = { + .name = "ffa-crb", + .probe = tpm_crb_ffa_probe, + .remove = tpm_crb_ffa_remove, + .id_table = tpm_crb_ffa_device_id, +}; + +module_ffa_driver(tpm_crb_ffa_driver); + +MODULE_AUTHOR("Arm"); +MODULE_DESCRIPTION("TPM CRB FFA driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/tpm/tpm_crb_ffa.h b/drivers/char/tpm/tpm_crb_ffa.h new file mode 100644 index 000000000000..d7e1344ea003 --- /dev/null +++ b/drivers/char/tpm/tpm_crb_ffa.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2024 Arm Ltd. + * + * This device driver implements the TPM CRB start method + * as defined in the TPM Service Command Response Buffer + * Interface Over FF-A (DEN0138). + */ +#ifndef _TPM_CRB_FFA_H +#define _TPM_CRB_FFA_H + +#if IS_REACHABLE(CONFIG_TCG_ARM_CRB_FFA) +int tpm_crb_ffa_init(void); +int tpm_crb_ffa_start(int request_type, int locality); +#else +static inline int tpm_crb_ffa_init(void) { return 0; } +static inline int tpm_crb_ffa_start(int request_type, int locality) { return 0; } +#endif + +#define CRB_FFA_START_TYPE_COMMAND 0 +#define CRB_FFA_START_TYPE_LOCALITY_REQUEST 1 + +#endif diff --git a/drivers/char/tpm/tpm_ftpm_tee.c b/drivers/char/tpm/tpm_ftpm_tee.c index 139556b21cc6..b82490439633 100644 --- a/drivers/char/tpm/tpm_ftpm_tee.c +++ b/drivers/char/tpm/tpm_ftpm_tee.c @@ -31,45 +31,19 @@ static const uuid_t ftpm_ta_uuid = 0x82, 0xCB, 0x34, 0x3F, 0xB7, 0xF3, 0x78, 0x96); /** - * ftpm_tee_tpm_op_recv() - retrieve fTPM response. - * @chip: the tpm_chip description as specified in driver/char/tpm/tpm.h. - * @buf: the buffer to store data. - * @count: the number of bytes to read. - * - * Return: - * In case of success the number of bytes received. - * On failure, -errno. - */ -static int ftpm_tee_tpm_op_recv(struct tpm_chip *chip, u8 *buf, size_t count) -{ - struct ftpm_tee_private *pvt_data = dev_get_drvdata(chip->dev.parent); - size_t len; - - len = pvt_data->resp_len; - if (count < len) { - dev_err(&chip->dev, - "%s: Invalid size in recv: count=%zd, resp_len=%zd\n", - __func__, count, len); - return -EIO; - } - - memcpy(buf, pvt_data->resp_buf, len); - pvt_data->resp_len = 0; - - return len; -} - -/** - * ftpm_tee_tpm_op_send() - send TPM commands through the TEE shared memory. + * ftpm_tee_tpm_op_send() - send TPM commands through the TEE shared memory + * and retrieve the response. * @chip: the tpm_chip description as specified in driver/char/tpm/tpm.h - * @buf: the buffer to send. - * @len: the number of bytes to send. + * @buf: the buffer to send and to store the response. + * @bufsiz: the size of the buffer. + * @cmd_len: the number of bytes to send. * * Return: - * In case of success, returns 0. + * In case of success, returns the number of bytes received. * On failure, -errno */ -static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t len) +static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t cmd_len) { struct ftpm_tee_private *pvt_data = dev_get_drvdata(chip->dev.parent); size_t resp_len; @@ -80,16 +54,15 @@ static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t len) struct tee_param command_params[4]; struct tee_shm *shm = pvt_data->shm; - if (len > MAX_COMMAND_SIZE) { + if (cmd_len > MAX_COMMAND_SIZE) { dev_err(&chip->dev, "%s: len=%zd exceeds MAX_COMMAND_SIZE supported by fTPM TA\n", - __func__, len); + __func__, cmd_len); return -EIO; } memset(&transceive_args, 0, sizeof(transceive_args)); memset(command_params, 0, sizeof(command_params)); - pvt_data->resp_len = 0; /* Invoke FTPM_OPTEE_TA_SUBMIT_COMMAND function of fTPM TA */ transceive_args = (struct tee_ioctl_invoke_arg) { @@ -103,7 +76,7 @@ static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t len) .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT, .u.memref = { .shm = shm, - .size = len, + .size = cmd_len, .shm_offs = 0, }, }; @@ -115,7 +88,7 @@ static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t len) return PTR_ERR(temp_buf); } memset(temp_buf, 0, (MAX_COMMAND_SIZE + MAX_RESPONSE_SIZE)); - memcpy(temp_buf, buf, len); + memcpy(temp_buf, buf, cmd_len); command_params[1] = (struct tee_param) { .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT, @@ -156,38 +129,21 @@ static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t len) __func__, resp_len); return -EIO; } + if (resp_len > bufsiz) { + dev_err(&chip->dev, + "%s: resp_len=%zd exceeds bufsiz=%zd\n", + __func__, resp_len, bufsiz); + return -EIO; + } - /* sanity checks look good, cache the response */ - memcpy(pvt_data->resp_buf, temp_buf, resp_len); - pvt_data->resp_len = resp_len; - - return 0; -} - -static void ftpm_tee_tpm_op_cancel(struct tpm_chip *chip) -{ - /* not supported */ -} - -static u8 ftpm_tee_tpm_op_status(struct tpm_chip *chip) -{ - return 0; -} + memcpy(buf, temp_buf, resp_len); -static bool ftpm_tee_tpm_req_canceled(struct tpm_chip *chip, u8 status) -{ - return false; + return resp_len; } static const struct tpm_class_ops ftpm_tee_tpm_ops = { .flags = TPM_OPS_AUTO_STARTUP, - .recv = ftpm_tee_tpm_op_recv, .send = ftpm_tee_tpm_op_send, - .cancel = ftpm_tee_tpm_op_cancel, - .status = ftpm_tee_tpm_op_status, - .req_complete_mask = 0, - .req_complete_val = 0, - .req_canceled = ftpm_tee_tpm_req_canceled, }; /* @@ -207,13 +163,13 @@ static int ftpm_tee_match(struct tee_ioctl_version_data *ver, const void *data) } /** - * ftpm_tee_probe() - initialize the fTPM + * ftpm_tee_probe_generic() - initialize the fTPM * @dev: the device description. * * Return: * On success, 0. On failure, -errno. */ -static int ftpm_tee_probe(struct device *dev) +static int ftpm_tee_probe_generic(struct device *dev) { int rc; struct tpm_chip *chip; @@ -271,7 +227,7 @@ static int ftpm_tee_probe(struct device *dev) } pvt_data->chip = chip; - pvt_data->chip->flags |= TPM_CHIP_FLAG_TPM2; + pvt_data->chip->flags |= TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_SYNC; /* Create a character device for the fTPM */ rc = tpm_chip_register(pvt_data->chip); @@ -295,21 +251,28 @@ out_tee_session: return rc; } +static int ftpm_tee_probe(struct tee_client_device *tcdev) +{ + struct device *dev = &tcdev->dev; + + return ftpm_tee_probe_generic(dev); +} + static int ftpm_plat_tee_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - return ftpm_tee_probe(dev); + return ftpm_tee_probe_generic(dev); } /** - * ftpm_tee_remove() - remove the TPM device + * ftpm_tee_remove_generic() - remove the TPM device * @dev: the device description. * * Return: * 0 always. */ -static int ftpm_tee_remove(struct device *dev) +static void ftpm_tee_remove_generic(struct device *dev) { struct ftpm_tee_private *pvt_data = dev_get_drvdata(dev); @@ -329,15 +292,20 @@ static int ftpm_tee_remove(struct device *dev) tee_client_close_context(pvt_data->ctx); /* memory allocated with devm_kzalloc() is freed automatically */ +} - return 0; +static void ftpm_tee_remove(struct tee_client_device *tcdev) +{ + struct device *dev = &tcdev->dev; + + ftpm_tee_remove_generic(dev); } static void ftpm_plat_tee_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; - ftpm_tee_remove(dev); + ftpm_tee_remove_generic(dev); } /** @@ -362,7 +330,7 @@ MODULE_DEVICE_TABLE(of, of_ftpm_tee_ids); static struct platform_driver ftpm_tee_plat_driver = { .driver = { .name = "ftpm-tee", - .of_match_table = of_match_ptr(of_ftpm_tee_ids), + .of_match_table = of_ftpm_tee_ids, }, .shutdown = ftpm_plat_tee_shutdown, .probe = ftpm_plat_tee_probe, @@ -379,12 +347,11 @@ static const struct tee_client_device_id optee_ftpm_id_table[] = { MODULE_DEVICE_TABLE(tee, optee_ftpm_id_table); static struct tee_client_driver ftpm_tee_driver = { + .probe = ftpm_tee_probe, + .remove = ftpm_tee_remove, .id_table = optee_ftpm_id_table, .driver = { .name = "optee-ftpm", - .bus = &tee_bus_type, - .probe = ftpm_tee_probe, - .remove = ftpm_tee_remove, }, }; @@ -396,7 +363,7 @@ static int __init ftpm_mod_init(void) if (rc) return rc; - rc = driver_register(&ftpm_tee_driver.driver); + rc = tee_client_driver_register(&ftpm_tee_driver); if (rc) { platform_driver_unregister(&ftpm_tee_plat_driver); return rc; @@ -408,7 +375,7 @@ static int __init ftpm_mod_init(void) static void __exit ftpm_mod_exit(void) { platform_driver_unregister(&ftpm_tee_plat_driver); - driver_unregister(&ftpm_tee_driver.driver); + tee_client_driver_unregister(&ftpm_tee_driver); } module_init(ftpm_mod_init); diff --git a/drivers/char/tpm/tpm_ftpm_tee.h b/drivers/char/tpm/tpm_ftpm_tee.h index f98daa7bf68c..8d5c3f0d2879 100644 --- a/drivers/char/tpm/tpm_ftpm_tee.h +++ b/drivers/char/tpm/tpm_ftpm_tee.h @@ -21,18 +21,13 @@ /** * struct ftpm_tee_private - fTPM's private data * @chip: struct tpm_chip instance registered with tpm framework. - * @state: internal state * @session: fTPM TA session identifier. - * @resp_len: cached response buffer length. - * @resp_buf: cached response buffer. * @ctx: TEE context handler. * @shm: Memory pool shared with fTPM TA in TEE. */ struct ftpm_tee_private { struct tpm_chip *chip; u32 session; - size_t resp_len; - u8 resp_buf[MAX_RESPONSE_SIZE]; struct tee_context *ctx; struct tee_shm *shm; }; diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c index d1d27fdfe523..9fd73049821f 100644 --- a/drivers/char/tpm/tpm_i2c_atmel.c +++ b/drivers/char/tpm/tpm_i2c_atmel.c @@ -31,13 +31,16 @@ struct priv_data { size_t len; - /* This is the amount we read on the first try. 25 was chosen to fit a + /* + * This is the amount we read on the first try. 25 was chosen to fit a * fair number of read responses in the buffer so a 2nd retry can be - * avoided in small message cases. */ + * avoided in small message cases. + */ u8 buffer[sizeof(struct tpm_header) + 25]; }; -static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len) +static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t len) { struct priv_data *priv = dev_get_drvdata(&chip->dev); struct i2c_client *client = to_i2c_client(chip->dev.parent); @@ -57,7 +60,9 @@ static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len) if (status < 0) return status; - /* The upper layer does not support incomplete sends. */ + /* + * The upper layer does not support incomplete sends. + */ if (status != len) return -E2BIG; @@ -75,9 +80,11 @@ static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count) if (priv->len == 0) return -EIO; - /* Get the message size from the message header, if we didn't get the + /* + * Get the message size from the message header, if we didn't get the * whole message in read_status then we need to re-read the - * message. */ + * message. + */ expected_len = be32_to_cpu(hdr->length); if (expected_len > count) return -ENOMEM; @@ -110,15 +117,19 @@ static u8 i2c_atmel_read_status(struct tpm_chip *chip) struct i2c_client *client = to_i2c_client(chip->dev.parent); int rc; - /* The TPM fails the I2C read until it is ready, so we do the entire + /* + * The TPM fails the I2C read until it is ready, so we do the entire * transfer here and buffer it locally. This way the common code can - * properly handle the timeouts. */ + * properly handle the timeouts. + */ priv->len = 0; memset(priv->buffer, 0, sizeof(priv->buffer)); - /* Once the TPM has completed the command the command remains readable - * until another command is issued. */ + /* + * Once the TPM has completed the command the command remains readable + * until another command is issued. + */ rc = i2c_master_recv(client, priv->buffer, sizeof(priv->buffer)); dev_dbg(&chip->dev, "%s: sts=%d", __func__, rc); @@ -171,9 +182,11 @@ static int i2c_atmel_probe(struct i2c_client *client) dev_set_drvdata(&chip->dev, priv); - /* There is no known way to probe for this device, and all version + /* + * There is no known way to probe for this device, and all version * information seems to be read via TPM commands. Thus we rely on the - * TPM startup process in the common code to detect the device. */ + * TPM startup process in the common code to detect the device. + */ return tpm_chip_register(chip); } diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index 81d8a78dc655..8b7d32de0b2e 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -514,7 +514,8 @@ out: return size; } -static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) +static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t len) { int rc, status; ssize_t burstcnt; @@ -543,8 +544,10 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) burstcnt = get_burstcount(chip); /* burstcnt < 0 = TPM is busy */ - if (burstcnt < 0) - return burstcnt; + if (burstcnt < 0) { + rc = burstcnt; + goto out_err; + } if (burstcnt > (len - 1 - count)) burstcnt = len - 1 - count; diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index 3c3ee5f551db..d44903b29929 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -350,7 +350,8 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count) * tpm.c can skip polling for the data to be available as the interrupt is * waited for here */ -static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len) +static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t len) { struct priv_data *priv = dev_get_drvdata(&chip->dev); struct device *dev = chip->dev.parent; diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c index 76d048f63d55..6fb4d30eb6f6 100644 --- a/drivers/char/tpm/tpm_ibmvtpm.c +++ b/drivers/char/tpm/tpm_ibmvtpm.c @@ -191,13 +191,15 @@ static int tpm_ibmvtpm_resume(struct device *dev) * tpm_ibmvtpm_send() - Send a TPM command * @chip: tpm chip struct * @buf: buffer contains data to send - * @count: size of buffer + * @bufsiz: size of the buffer + * @count: length of the command * * Return: * 0 on success, * -errno on error */ -static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) +static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t count) { struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev); bool retry = true; @@ -609,7 +611,7 @@ static int tpm_ibmvtpm_probe(struct vio_dev *vio_dev, if (IS_ERR(chip)) return PTR_ERR(chip); - ibmvtpm = kzalloc(sizeof(struct ibmvtpm_dev), GFP_KERNEL); + ibmvtpm = kzalloc_obj(struct ibmvtpm_dev); if (!ibmvtpm) { dev_err(dev, "kzalloc for ibmvtpm failed\n"); goto cleanup; diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c index 2d2ae37153ba..7638b65b851b 100644 --- a/drivers/char/tpm/tpm_infineon.c +++ b/drivers/char/tpm/tpm_infineon.c @@ -312,7 +312,8 @@ recv_begin: return -EIO; } -static int tpm_inf_send(struct tpm_chip *chip, u8 * buf, size_t count) +static int tpm_inf_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t count) { int i; int ret; diff --git a/drivers/char/tpm/tpm_loongson.c b/drivers/char/tpm/tpm_loongson.c new file mode 100644 index 000000000000..9e50250763d1 --- /dev/null +++ b/drivers/char/tpm/tpm_loongson.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Loongson Technology Corporation Limited. */ + +#include <linux/device.h> +#include <linux/mfd/loongson-se.h> +#include <linux/platform_device.h> +#include <linux/wait.h> + +#include "tpm.h" + +struct tpm_loongson_cmd { + u32 cmd_id; + u32 data_off; + u32 data_len; + u32 pad[5]; +}; + +static int tpm_loongson_recv(struct tpm_chip *chip, u8 *buf, size_t count) +{ + struct loongson_se_engine *tpm_engine = dev_get_drvdata(&chip->dev); + struct tpm_loongson_cmd *cmd_ret = tpm_engine->command_ret; + + if (cmd_ret->data_len > count) + return -EIO; + + memcpy(buf, tpm_engine->data_buffer, cmd_ret->data_len); + + return cmd_ret->data_len; +} + +static int tpm_loongson_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, size_t count) +{ + struct loongson_se_engine *tpm_engine = dev_get_drvdata(&chip->dev); + struct tpm_loongson_cmd *cmd = tpm_engine->command; + + if (count > tpm_engine->buffer_size) + return -E2BIG; + + cmd->data_len = count; + memcpy(tpm_engine->data_buffer, buf, count); + + return loongson_se_send_engine_cmd(tpm_engine); +} + +static const struct tpm_class_ops tpm_loongson_ops = { + .flags = TPM_OPS_AUTO_STARTUP, + .recv = tpm_loongson_recv, + .send = tpm_loongson_send, +}; + +static int tpm_loongson_probe(struct platform_device *pdev) +{ + struct loongson_se_engine *tpm_engine; + struct device *dev = &pdev->dev; + struct tpm_loongson_cmd *cmd; + struct tpm_chip *chip; + + tpm_engine = loongson_se_init_engine(dev->parent, SE_ENGINE_TPM); + if (!tpm_engine) + return -ENODEV; + cmd = tpm_engine->command; + cmd->cmd_id = SE_CMD_TPM; + cmd->data_off = tpm_engine->buffer_off; + + chip = tpmm_chip_alloc(dev, &tpm_loongson_ops); + if (IS_ERR(chip)) + return PTR_ERR(chip); + chip->flags = TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_IRQ; + dev_set_drvdata(&chip->dev, tpm_engine); + + return tpm_chip_register(chip); +} + +static struct platform_driver tpm_loongson = { + .probe = tpm_loongson_probe, + .driver = { + .name = "tpm_loongson", + }, +}; +module_platform_driver(tpm_loongson); + +MODULE_ALIAS("platform:tpm_loongson"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Loongson TPM driver"); diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c index 0f62bbc940da..879ac88f5783 100644 --- a/drivers/char/tpm/tpm_nsc.c +++ b/drivers/char/tpm/tpm_nsc.c @@ -178,7 +178,8 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count) return size; } -static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count) +static int tpm_nsc_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t count) { struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev); u8 data; diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c index bc7b1b4501b3..c9793a3d986d 100644 --- a/drivers/char/tpm/tpm_ppi.c +++ b/drivers/char/tpm/tpm_ppi.c @@ -33,6 +33,20 @@ static const guid_t tpm_ppi_guid = GUID_INIT(0x3DDDFAA6, 0x361B, 0x4EB4, 0xA4, 0x24, 0x8D, 0x10, 0x08, 0x9D, 0x16, 0x53); +static const char * const tpm_ppi_info[] = { + "Not implemented", + "BIOS only", + "Blocked for OS by system firmware", + "User required", + "User not required", +}; + +/* A spinlock to protect access to the cache from concurrent reads */ +static DEFINE_MUTEX(tpm_ppi_lock); + +static u32 ppi_operations_cache[PPI_VS_REQ_END + 1]; +static bool ppi_cache_populated; + static bool tpm_ppi_req_has_parameter(u64 req) { return req == 23; @@ -52,7 +66,7 @@ static ssize_t tpm_show_ppi_version(struct device *dev, { struct tpm_chip *chip = to_tpm_chip(dev); - return scnprintf(buf, PAGE_SIZE, "%s\n", chip->ppi_version); + return sysfs_emit(buf, "%s\n", chip->ppi_version); } static ssize_t tpm_show_ppi_request(struct device *dev, @@ -87,12 +101,10 @@ static ssize_t tpm_show_ppi_request(struct device *dev, else { req = obj->package.elements[1].integer.value; if (tpm_ppi_req_has_parameter(req)) - size = scnprintf(buf, PAGE_SIZE, - "%llu %llu\n", req, - obj->package.elements[2].integer.value); + size = sysfs_emit(buf, "%llu %llu\n", req, + obj->package.elements[2].integer.value); else - size = scnprintf(buf, PAGE_SIZE, - "%llu\n", req); + size = sysfs_emit(buf, "%llu\n", req); } } else if (obj->package.count == 2 && obj->package.elements[0].type == ACPI_TYPE_INTEGER && @@ -100,8 +112,8 @@ static ssize_t tpm_show_ppi_request(struct device *dev, if (obj->package.elements[0].integer.value) size = -EFAULT; else - size = scnprintf(buf, PAGE_SIZE, "%llu\n", - obj->package.elements[1].integer.value); + size = sysfs_emit(buf, "%llu\n", + obj->package.elements[1].integer.value); } ACPI_FREE(obj); @@ -211,10 +223,10 @@ static ssize_t tpm_show_ppi_transition_action(struct device *dev, } if (ret < ARRAY_SIZE(info) - 1) - status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret, info[ret]); + status = sysfs_emit(buf, "%d: %s\n", ret, info[ret]); else - status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret, - info[ARRAY_SIZE(info)-1]); + status = sysfs_emit(buf, "%d: %s\n", ret, + info[ARRAY_SIZE(info) - 1]); return status; } @@ -255,23 +267,23 @@ static ssize_t tpm_show_ppi_response(struct device *dev, res = ret_obj[2].integer.value; if (req) { if (res == 0) - status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req, - "0: Success"); + status = sysfs_emit(buf, "%llu %s\n", req, + "0: Success"); else if (res == 0xFFFFFFF0) - status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req, - "0xFFFFFFF0: User Abort"); + status = sysfs_emit(buf, "%llu %s\n", req, + "0xFFFFFFF0: User Abort"); else if (res == 0xFFFFFFF1) - status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req, - "0xFFFFFFF1: BIOS Failure"); + status = sysfs_emit(buf, "%llu %s\n", req, + "0xFFFFFFF1: BIOS Failure"); else if (res >= 1 && res <= 0x00000FFF) - status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n", - req, res, "Corresponding TPM error"); + status = sysfs_emit(buf, "%llu %llu: %s\n", + req, res, "Corresponding TPM error"); else - status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n", - req, res, "Error"); + status = sysfs_emit(buf, "%llu %llu: %s\n", + req, res, "Error"); } else { - status = scnprintf(buf, PAGE_SIZE, "%llu: %s\n", - req, "No Recent Request"); + status = sysfs_emit(buf, "%llu: %s\n", + req, "No Recent Request"); } cleanup: @@ -279,46 +291,33 @@ cleanup: return status; } -static ssize_t show_ppi_operations(acpi_handle dev_handle, char *buf, u32 start, - u32 end) +static ssize_t cache_ppi_operations(acpi_handle dev_handle, char *buf) { int i; u32 ret; - char *str = buf; + int len = 0; union acpi_object *obj, tmp; union acpi_object argv = ACPI_INIT_DSM_ARGV4(1, &tmp); - static char *info[] = { - "Not implemented", - "BIOS only", - "Blocked for OS by BIOS", - "User required", - "User not required", - }; - if (!acpi_check_dsm(dev_handle, &tpm_ppi_guid, TPM_PPI_REVISION_ID_1, 1 << TPM_PPI_FN_GETOPR)) return -EPERM; tmp.integer.type = ACPI_TYPE_INTEGER; - for (i = start; i <= end; i++) { + for (i = 0; i <= PPI_VS_REQ_END; i++) { tmp.integer.value = i; obj = tpm_eval_dsm(dev_handle, TPM_PPI_FN_GETOPR, ACPI_TYPE_INTEGER, &argv, TPM_PPI_REVISION_ID_1); - if (!obj) { + if (!obj) return -ENOMEM; - } else { - ret = obj->integer.value; - ACPI_FREE(obj); - } - if (ret > 0 && ret < ARRAY_SIZE(info)) - str += scnprintf(str, PAGE_SIZE, "%d %d: %s\n", - i, ret, info[ret]); + ret = obj->integer.value; + ppi_operations_cache[i] = ret; + ACPI_FREE(obj); } - return str - buf; + return len; } static ssize_t tpm_show_ppi_tcg_operations(struct device *dev, @@ -326,9 +325,30 @@ static ssize_t tpm_show_ppi_tcg_operations(struct device *dev, char *buf) { struct tpm_chip *chip = to_tpm_chip(dev); + ssize_t len = 0; + u32 ret; + int i; + + mutex_lock(&tpm_ppi_lock); + if (!ppi_cache_populated) { + len = cache_ppi_operations(chip->acpi_dev_handle, buf); + if (len < 0) { + mutex_unlock(&tpm_ppi_lock); + return len; + } + + ppi_cache_populated = true; + } - return show_ppi_operations(chip->acpi_dev_handle, buf, 0, - PPI_TPM_REQ_MAX); + for (i = 0; i <= PPI_TPM_REQ_MAX; i++) { + ret = ppi_operations_cache[i]; + if (ret >= 0 && ret < ARRAY_SIZE(tpm_ppi_info)) + len += sysfs_emit_at(buf, len, "%d %d: %s\n", + i, ret, tpm_ppi_info[ret]); + } + mutex_unlock(&tpm_ppi_lock); + + return len; } static ssize_t tpm_show_ppi_vs_operations(struct device *dev, @@ -336,9 +356,30 @@ static ssize_t tpm_show_ppi_vs_operations(struct device *dev, char *buf) { struct tpm_chip *chip = to_tpm_chip(dev); + ssize_t len = 0; + u32 ret; + int i; + + mutex_lock(&tpm_ppi_lock); + if (!ppi_cache_populated) { + len = cache_ppi_operations(chip->acpi_dev_handle, buf); + if (len < 0) { + mutex_unlock(&tpm_ppi_lock); + return len; + } + + ppi_cache_populated = true; + } + + for (i = PPI_VS_REQ_START; i <= PPI_VS_REQ_END; i++) { + ret = ppi_operations_cache[i]; + if (ret >= 0 && ret < ARRAY_SIZE(tpm_ppi_info)) + len += sysfs_emit_at(buf, len, "%d %d: %s\n", + i, ret, tpm_ppi_info[ret]); + } + mutex_unlock(&tpm_ppi_lock); - return show_ppi_operations(chip->acpi_dev_handle, buf, PPI_VS_REQ_START, - PPI_VS_REQ_END); + return len; } static DEVICE_ATTR(version, S_IRUGO, tpm_show_ppi_version, NULL); diff --git a/drivers/char/tpm/tpm_svsm.c b/drivers/char/tpm/tpm_svsm.c new file mode 100644 index 000000000000..b74d60f687d5 --- /dev/null +++ b/drivers/char/tpm/tpm_svsm.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. + * + * Driver for the vTPM defined by the AMD SVSM spec [1]. + * + * The specification defines a protocol that a SEV-SNP guest OS can use to + * discover and talk to a vTPM emulated by the Secure VM Service Module (SVSM) + * in the guest context, but at a more privileged level (usually VMPL0). + * + * [1] "Secure VM Service Module for SEV-SNP Guests" + * Publication # 58019 Revision: 1.00 + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/tpm_svsm.h> + +#include <asm/sev.h> + +#include "tpm.h" + +struct tpm_svsm_priv { + void *buffer; +}; + +static int tpm_svsm_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t cmd_len) +{ + struct tpm_svsm_priv *priv = dev_get_drvdata(&chip->dev); + int ret; + + ret = svsm_vtpm_cmd_request_fill(priv->buffer, 0, buf, cmd_len); + if (ret) + return ret; + + /* + * The SVSM call uses the same buffer for the command and for the + * response, so after this call, the buffer will contain the response. + * + * Note: we have to use an internal buffer because the device in SVSM + * expects the svsm_vtpm header + data to be physically contiguous. + */ + ret = snp_svsm_vtpm_send_command(priv->buffer); + if (ret) + return ret; + + return svsm_vtpm_cmd_response_parse(priv->buffer, buf, bufsiz); +} + +static const struct tpm_class_ops tpm_chip_ops = { + .flags = TPM_OPS_AUTO_STARTUP, + .send = tpm_svsm_send, +}; + +static int __init tpm_svsm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tpm_svsm_priv *priv; + struct tpm_chip *chip; + int err; + + priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* + * The maximum buffer supported is one page (see SVSM_VTPM_MAX_BUFFER + * in tpm_svsm.h). + */ + priv->buffer = (void *)devm_get_free_pages(dev, GFP_KERNEL, 0); + if (!priv->buffer) + return -ENOMEM; + + chip = tpmm_chip_alloc(dev, &tpm_chip_ops); + if (IS_ERR(chip)) + return PTR_ERR(chip); + + dev_set_drvdata(&chip->dev, priv); + + chip->flags |= TPM_CHIP_FLAG_SYNC; + err = tpm2_probe(chip); + if (err) + return err; + + err = tpm_chip_register(chip); + if (err) + return err; + + dev_info(dev, "SNP SVSM vTPM %s device\n", + (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2"); + + return 0; +} + +static void __exit tpm_svsm_remove(struct platform_device *pdev) +{ + struct tpm_chip *chip = platform_get_drvdata(pdev); + + tpm_chip_unregister(chip); +} + +/* + * tpm_svsm_remove() lives in .exit.text. For drivers registered via + * module_platform_driver_probe() this is ok because they cannot get unbound + * at runtime. So mark the driver struct with __refdata to prevent modpost + * triggering a section mismatch warning. + */ +static struct platform_driver tpm_svsm_driver __refdata = { + .remove = __exit_p(tpm_svsm_remove), + .driver = { + .name = "tpm-svsm", + }, +}; + +module_platform_driver_probe(tpm_svsm_driver, tpm_svsm_probe); + +MODULE_DESCRIPTION("SNP SVSM vTPM Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:tpm-svsm"); diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index fdef214b9f6b..143ef160820e 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -66,8 +66,8 @@ static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, bool check_cancel) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + DEFINE_WAIT_FUNC(wait, woken_wake_function); unsigned long stop; - long rc; u8 status; bool canceled = false; u8 sts_mask; @@ -87,23 +87,30 @@ static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, /* process status changes with irq support */ if (sts_mask) { ret = -ETIME; + add_wait_queue(queue, &wait); again: + if (wait_for_tpm_stat_cond(chip, sts_mask, check_cancel, + &canceled)) { + ret = canceled ? -ECANCELED : 0; + goto out; + } + timeout = stop - jiffies; if ((long)timeout <= 0) - return -ETIME; - rc = wait_event_interruptible_timeout(*queue, - wait_for_tpm_stat_cond(chip, sts_mask, check_cancel, - &canceled), - timeout); - if (rc > 0) { - if (canceled) - return -ECANCELED; - ret = 0; - } - if (rc == -ERESTARTSYS && freezing(current)) { - clear_thread_flag(TIF_SIGPENDING); - goto again; + goto out; + + if (signal_pending(current)) { + if (freezing(current)) { + clear_thread_flag(TIF_SIGPENDING); + goto again; + } + goto out; } + + wait_woken(&wait, TASK_INTERRUPTIBLE, timeout); + goto again; +out: + remove_wait_queue(queue, &wait); } if (ret) @@ -114,11 +121,10 @@ again: return 0; /* process status changes without irq support */ do { + usleep_range(priv->timeout_min, priv->timeout_max); status = chip->ops->status(chip); if ((status & mask) == mask) return 0; - usleep_range(priv->timeout_min, - priv->timeout_max); } while (time_before(jiffies, stop)); return -ETIME; } @@ -172,6 +178,9 @@ static int __tpm_tis_relinquish_locality(struct tpm_tis_data *priv, int l) { tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY); + if (test_bit(TPM_TIS_SETTLE_AFTER_RELINQUISH, &priv->flags)) + tpm_msleep(TPM_TIMEOUT); + return 0; } @@ -266,8 +275,7 @@ static u8 tpm_tis_status(struct tpm_chip *chip) /* * Dump stack for forensics, as invalid TPM_STS.x could be - * potentially triggered by impaired tpm_try_get_ops() or - * tpm_find_get_ops(). + * potentially triggered by impaired tpm_try_get_ops(). */ dump_stack(); } @@ -464,12 +472,17 @@ static int tpm_tis_send_data(struct tpm_chip *chip, const u8 *buf, size_t len) if (wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c, &priv->int_queue, false) < 0) { - rc = -ETIME; + if (test_bit(TPM_TIS_STATUS_VALID_RETRY, &priv->flags)) + rc = -EAGAIN; + else + rc = -ETIME; goto out_err; } status = tpm_tis_status(chip); if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) { rc = -EIO; + dev_err(&chip->dev, "TPM_STS_DATA_EXPECT should be set. sts = 0x%08x\n", + status); goto out_err; } } @@ -481,12 +494,17 @@ static int tpm_tis_send_data(struct tpm_chip *chip, const u8 *buf, size_t len) if (wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c, &priv->int_queue, false) < 0) { - rc = -ETIME; + if (test_bit(TPM_TIS_STATUS_VALID_RETRY, &priv->flags)) + rc = -EAGAIN; + else + rc = -ETIME; goto out_err; } status = tpm_tis_status(chip); if (!itpm && (status & TPM_STS_DATA_EXPECT) != 0) { rc = -EIO; + dev_err(&chip->dev, "TPM_STS_DATA_EXPECT should be unset. sts = 0x%08x\n", + status); goto out_err; } @@ -546,9 +564,16 @@ static int tpm_tis_send_main(struct tpm_chip *chip, const u8 *buf, size_t len) if (rc >= 0) /* Data transfer done successfully */ break; - else if (rc != -EIO) + else if (rc != -EAGAIN && rc != -EIO) /* Data transfer failed, not recoverable */ - return rc; + goto out_err; + + usleep_range(priv->timeout_min, priv->timeout_max); + } + + if (rc == -EAGAIN || rc == -EIO) { + dev_err(&chip->dev, "Exhausted %d tpm_tis_send_data retries\n", TPM_RETRY); + goto out_err; } /* go and do it */ @@ -573,7 +598,8 @@ out_err: return rc; } -static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) +static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t len) { int rc, irq; struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); @@ -776,9 +802,10 @@ out: static bool tpm_tis_req_canceled(struct tpm_chip *chip, u8 status) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + u16 vendor_id = priv->did_vid; if (!test_bit(TPM_TIS_DEFAULT_CANCELLATION, &priv->flags)) { - switch (priv->manufacturer_id) { + switch (vendor_id) { case TPM_VID_WINBOND: return ((status == TPM_STS_VALID) || (status == (TPM_STS_VALID | TPM_STS_COMMAND_READY))); @@ -970,8 +997,8 @@ restore_irqs: * will call disable_irq which undoes all of the above. */ if (!(chip->flags & TPM_CHIP_FLAG_IRQ)) { - tpm_tis_write8(priv, original_int_vec, - TPM_INT_VECTOR(priv->locality)); + tpm_tis_write8(priv, TPM_INT_VECTOR(priv->locality), + original_int_vec); rc = -1; } @@ -1099,7 +1126,8 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, const struct tpm_tis_phy_ops *phy_ops, acpi_handle acpi_dev_handle) { - u32 vendor; + u16 vendor_id; + u16 device_id; u32 intfcaps; u32 intmask; u32 clkrun_val; @@ -1132,18 +1160,25 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, dev_set_drvdata(&chip->dev, priv); - rc = tpm_tis_read32(priv, TPM_DID_VID(0), &vendor); + rc = tpm_tis_read32(priv, TPM_DID_VID(0), &priv->did_vid); if (rc < 0) return rc; - priv->manufacturer_id = vendor; + vendor_id = priv->did_vid; + device_id = priv->did_vid >> 16; - if (priv->manufacturer_id == TPM_VID_ATML && + if (vendor_id == TPM_VID_ATML && !(chip->flags & TPM_CHIP_FLAG_TPM2)) { priv->timeout_min = TIS_TIMEOUT_MIN_ATML; priv->timeout_max = TIS_TIMEOUT_MAX_ATML; } + if (vendor_id == TPM_VID_IFX) + set_bit(TPM_TIS_STATUS_VALID_RETRY, &priv->flags); + + if (vendor_id == TPM_VID_WINBOND && device_id == 0x00FE) + set_bit(TPM_TIS_SETTLE_AFTER_RELINQUISH, &priv->flags); + if (is_bsw()) { priv->ilb_base_addr = ioremap(INTEL_LEGACY_BLK_BASE_ADDR, ILB_REMAP_SIZE); @@ -1228,9 +1263,9 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, if (rc < 0) goto out_err; - dev_info(dev, "%s TPM (device-id 0x%X, rev-id %d)\n", + dev_info(dev, "%s TPM (vendor-id 0x%X, device-id 0x%X, rev-id %d)\n", (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2", - vendor >> 16, rid); + vendor_id, device_id, rid); probe = probe_itpm(chip); if (probe < 0) { @@ -1296,6 +1331,9 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, return 0; out_err: + dev_err(dev, "TPM vid 0x%X, did 0x%X init failed with error %d\n", + vendor_id, device_id, rc); + if (chip->ops->clk_enable != NULL) chip->ops->clk_enable(chip, false); diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h index 690ad8e9b731..aa6d78898ef3 100644 --- a/drivers/char/tpm/tpm_tis_core.h +++ b/drivers/char/tpm/tpm_tis_core.h @@ -54,7 +54,7 @@ enum tis_int_flags { enum tis_defaults { TIS_MEM_LEN = 0x5000, TIS_SHORT_TIMEOUT = 750, /* ms */ - TIS_LONG_TIMEOUT = 2000, /* 2 sec */ + TIS_LONG_TIMEOUT = 4000, /* 4 secs */ TIS_TIMEOUT_MIN_ATML = 14700, /* usecs */ TIS_TIMEOUT_MAX_ATML = 15000, /* usecs */ }; @@ -89,11 +89,13 @@ enum tpm_tis_flags { TPM_TIS_INVALID_STATUS = 1, TPM_TIS_DEFAULT_CANCELLATION = 2, TPM_TIS_IRQ_TESTED = 3, + TPM_TIS_STATUS_VALID_RETRY = 4, + TPM_TIS_SETTLE_AFTER_RELINQUISH = 5, }; struct tpm_tis_data { struct tpm_chip *chip; - u16 manufacturer_id; + u32 did_vid; struct mutex locality_count_mutex; unsigned int locality_count; int locality; diff --git a/drivers/char/tpm/tpm_tis_i2c_cr50.c b/drivers/char/tpm/tpm_tis_i2c_cr50.c index 3b55a7b05c46..b48cacacc066 100644 --- a/drivers/char/tpm/tpm_tis_i2c_cr50.c +++ b/drivers/char/tpm/tpm_tis_i2c_cr50.c @@ -546,13 +546,15 @@ out_err: * tpm_cr50_i2c_tis_send() - TPM transmission callback. * @chip: A TPM chip. * @buf: Buffer to send. - * @len: Buffer length. + * @bufsiz: Buffer size. + * @len: Command length. * * Return: * - 0: Success. * - -errno: A POSIX error code. */ -static int tpm_cr50_i2c_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) +static int tpm_cr50_i2c_tis_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t len) { size_t burstcnt, limit, sent = 0; u8 tpm_go[4] = { TPM_STS_GO }; @@ -747,8 +749,7 @@ static int tpm_cr50_i2c_probe(struct i2c_client *client) if (client->irq > 0) { rc = devm_request_irq(dev, client->irq, tpm_cr50_i2c_int_handler, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT | - IRQF_NO_AUTOEN, + IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN, dev->driver->name, chip); if (rc < 0) { dev_err(dev, "Failed to probe IRQ %d\n", client->irq); diff --git a/drivers/char/tpm/tpm_tis_spi_cr50.c b/drivers/char/tpm/tpm_tis_spi_cr50.c index f4937280e940..32920b4cecfb 100644 --- a/drivers/char/tpm/tpm_tis_spi_cr50.c +++ b/drivers/char/tpm/tpm_tis_spi_cr50.c @@ -287,7 +287,7 @@ int cr50_spi_probe(struct spi_device *spi) if (spi->irq > 0) { ret = devm_request_irq(&spi->dev, spi->irq, cr50_spi_irq_handler, - IRQF_TRIGGER_RISING | IRQF_ONESHOT, + IRQF_TRIGGER_RISING, "cr50_spi", cr50_phy); if (ret < 0) { if (ret == -EPROBE_DEFER) diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c index 8fe4a01eea12..7bb0f4d4a2ed 100644 --- a/drivers/char/tpm/tpm_vtpm_proxy.c +++ b/drivers/char/tpm/tpm_vtpm_proxy.c @@ -321,12 +321,14 @@ static int vtpm_proxy_is_driver_command(struct tpm_chip *chip, * * @chip: tpm chip to use * @buf: send buffer + * @bufsiz: size of the buffer * @count: bytes to send * * Return: * 0 in case of success, negative error value otherwise. */ -static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t count) +static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t count) { struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev); @@ -489,7 +491,7 @@ static struct proxy_dev *vtpm_proxy_create_proxy_dev(void) struct tpm_chip *chip; int err; - proxy_dev = kzalloc(sizeof(*proxy_dev), GFP_KERNEL); + proxy_dev = kzalloc_obj(*proxy_dev); if (proxy_dev == NULL) return ERR_PTR(-ENOMEM); diff --git a/drivers/char/tpm/tpmrm-dev.c b/drivers/char/tpm/tpmrm-dev.c index c25df7ea064e..19e8f2779265 100644 --- a/drivers/char/tpm/tpmrm-dev.c +++ b/drivers/char/tpm/tpmrm-dev.c @@ -17,7 +17,7 @@ static int tpmrm_open(struct inode *inode, struct file *file) int rc; chip = container_of(inode->i_cdev, struct tpm_chip, cdevs); - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = kzalloc_obj(*priv); if (priv == NULL) return -ENOMEM; @@ -29,7 +29,7 @@ static int tpmrm_open(struct inode *inode, struct file *file) tpm_common_open(file, chip, &priv->priv, &priv->space); - return 0; + return nonseekable_open(inode, file); } static int tpmrm_release(struct inode *inode, struct file *file) diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c index 80cca3b83b22..3c0cce57c4ae 100644 --- a/drivers/char/tpm/xen-tpmfront.c +++ b/drivers/char/tpm/xen-tpmfront.c @@ -131,7 +131,8 @@ static size_t shr_data_offset(struct vtpm_shared_page *shr) return struct_size(shr, extra_pages, shr->nr_extra_pages); } -static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) +static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + size_t count) { struct tpm_private *priv = dev_get_drvdata(&chip->dev); struct vtpm_shared_page *shr = priv->shr; @@ -337,7 +338,7 @@ static int tpmfront_probe(struct xenbus_device *dev, struct tpm_private *priv; int rv; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = kzalloc_obj(*priv); if (!priv) { xenbus_dev_fatal(dev, -ENOMEM, "allocating priv structure"); return -ENOMEM; diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 18f92dd44d45..198b97314168 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -26,6 +26,7 @@ #include <linux/workqueue.h> #include <linux/module.h> #include <linux/dma-mapping.h> +#include <linux/string_choices.h> #include "../tty/hvc/hvc_console.h" #define is_rproc_enabled IS_ENABLED(CONFIG_REMOTEPROC) @@ -411,7 +412,7 @@ static struct port_buffer *alloc_buf(struct virtio_device *vdev, size_t buf_size * Allocate buffer and the sg list. The sg list array is allocated * directly after the port_buffer struct. */ - buf = kmalloc(struct_size(buf, sg, pages), GFP_KERNEL); + buf = kmalloc_flex(*buf, sg, pages); if (!buf) goto fail; @@ -1269,8 +1270,7 @@ static int port_debugfs_show(struct seq_file *s, void *data) seq_printf(s, "bytes_sent: %lu\n", port->stats.bytes_sent); seq_printf(s, "bytes_received: %lu\n", port->stats.bytes_received); seq_printf(s, "bytes_discarded: %lu\n", port->stats.bytes_discarded); - seq_printf(s, "is_console: %s\n", - is_console_port(port) ? "yes" : "no"); + seq_printf(s, "is_console: %s\n", str_yes_no(is_console_port(port))); seq_printf(s, "console_vtermno: %u\n", port->cons.vtermno); return 0; @@ -1321,12 +1321,11 @@ static void send_sigio_to_port(struct port *port) static int add_port(struct ports_device *portdev, u32 id) { - char debugfs_name[16]; struct port *port; dev_t devt; int err; - port = kmalloc(sizeof(*port), GFP_KERNEL); + port = kmalloc_obj(*port); if (!port) { err = -ENOMEM; goto fail; @@ -1424,9 +1423,7 @@ static int add_port(struct ports_device *portdev, u32 id) * Finally, create the debugfs file that we can use to * inspect a port's state at any time */ - snprintf(debugfs_name, sizeof(debugfs_name), "vport%up%u", - port->portdev->vdev->index, id); - port->debugfs_file = debugfs_create_file(debugfs_name, 0444, + port->debugfs_file = debugfs_create_file(dev_name(port->dev), 0444, pdrvdata.debugfs_dir, port, &port_debugfs_fops); return 0; @@ -1579,8 +1576,8 @@ static void handle_control_message(struct virtio_device *vdev, break; case VIRTIO_CONSOLE_RESIZE: { struct { - __u16 rows; - __u16 cols; + __virtio16 cols; + __virtio16 rows; } size; if (!is_console_port(port)) @@ -1588,7 +1585,8 @@ static void handle_control_message(struct virtio_device *vdev, memcpy(&size, buf->buf + buf->offset + sizeof(*cpkt), sizeof(size)); - set_console_size(port, size.rows, size.cols); + set_console_size(port, virtio16_to_cpu(vdev, size.rows), + virtio16_to_cpu(vdev, size.cols)); port->cons.hvc->irq_requested = 1; resize_console(port); @@ -1773,32 +1771,40 @@ static void config_intr(struct virtio_device *vdev) schedule_work(&portdev->config_work); } -static void config_work_handler(struct work_struct *work) +static void update_size_from_config(struct ports_device *portdev) { - struct ports_device *portdev; + struct virtio_device *vdev; + struct port *port; + u16 rows, cols; - portdev = container_of(work, struct ports_device, config_work); - if (!use_multiport(portdev)) { - struct virtio_device *vdev; - struct port *port; - u16 rows, cols; + vdev = portdev->vdev; - vdev = portdev->vdev; - virtio_cread(vdev, struct virtio_console_config, cols, &cols); - virtio_cread(vdev, struct virtio_console_config, rows, &rows); + /* + * We'll use this way of resizing only for legacy support. + * For multiport devices, use control messages to indicate + * console size changes so that it can be done per-port. + * + * Don't test F_SIZE at all if we're rproc: not a valid feature. + */ + if (is_rproc_serial(vdev) || + use_multiport(portdev) || + !virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) + return; - port = find_port_by_id(portdev, 0); - set_console_size(port, rows, cols); + virtio_cread(vdev, struct virtio_console_config, cols, &cols); + virtio_cread(vdev, struct virtio_console_config, rows, &rows); - /* - * We'll use this way of resizing only for legacy - * support. For newer userspace - * (VIRTIO_CONSOLE_F_MULTPORT+), use control messages - * to indicate console size changes so that it can be - * done per-port. - */ - resize_console(port); - } + port = find_port_by_id(portdev, 0); + set_console_size(port, rows, cols); + resize_console(port); +} + +static void config_work_handler(struct work_struct *work) +{ + struct ports_device *portdev; + + portdev = container_of(work, struct ports_device, config_work); + update_size_from_config(portdev); } static int init_vqs(struct ports_device *portdev) @@ -1811,12 +1817,10 @@ static int init_vqs(struct ports_device *portdev) nr_ports = portdev->max_nr_ports; nr_queues = use_multiport(portdev) ? (nr_ports + 1) * 2 : 2; - vqs = kmalloc_array(nr_queues, sizeof(struct virtqueue *), GFP_KERNEL); - vqs_info = kcalloc(nr_queues, sizeof(*vqs_info), GFP_KERNEL); - portdev->in_vqs = kmalloc_array(nr_ports, sizeof(struct virtqueue *), - GFP_KERNEL); - portdev->out_vqs = kmalloc_array(nr_ports, sizeof(struct virtqueue *), - GFP_KERNEL); + vqs = kmalloc_objs(struct virtqueue *, nr_queues); + vqs_info = kzalloc_objs(*vqs_info, nr_queues); + portdev->in_vqs = kmalloc_objs(struct virtqueue *, nr_ports); + portdev->out_vqs = kmalloc_objs(struct virtqueue *, nr_ports); if (!vqs || !vqs_info || !portdev->in_vqs || !portdev->out_vqs) { err = -ENOMEM; goto free; @@ -1967,7 +1971,7 @@ static int virtcons_probe(struct virtio_device *vdev) return -EINVAL; } - portdev = kmalloc(sizeof(*portdev), GFP_KERNEL); + portdev = kmalloc_obj(*portdev); if (!portdev) { err = -ENOMEM; goto fail; @@ -2056,6 +2060,8 @@ static int virtcons_probe(struct virtio_device *vdev) __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, VIRTIO_CONSOLE_DEVICE_READY, 1); + update_size_from_config(portdev); + return 0; free_chrdev: diff --git a/drivers/char/xilinx_hwicap/fifo_icap.c b/drivers/char/xilinx_hwicap/fifo_icap.c index 619f3a30ec55..7bd786fa1be8 100644 --- a/drivers/char/xilinx_hwicap/fifo_icap.c +++ b/drivers/char/xilinx_hwicap/fifo_icap.c @@ -48,7 +48,7 @@ #define XHI_GIER_GIE_MASK 0x80000000 /* Global Interrupt enable Mask */ -/** +/* * HwIcap Device Interrupt Status/Enable Registers * * Interrupt Status Register (IPISR) : This register holds the @@ -102,6 +102,8 @@ static inline void fifo_icap_fifo_write(struct hwicap_drvdata *drvdata, * @drvdata: a pointer to the drvdata. * * This function will silently fail if the fifo is empty. + * + * Returns: 32-bit data from the Read FIFO **/ static inline u32 fifo_icap_fifo_read(struct hwicap_drvdata *drvdata) { @@ -156,6 +158,8 @@ static inline void fifo_icap_start_readback(struct hwicap_drvdata *drvdata) * D2 - Always 1 * D1 - Always 1 * D0 - Done bit + * + * Returns: the 32-bit ICAP status register **/ u32 fifo_icap_get_status(struct hwicap_drvdata *drvdata) { @@ -165,8 +169,11 @@ u32 fifo_icap_get_status(struct hwicap_drvdata *drvdata) } /** - * fifo_icap_busy - Return true if the ICAP is still processing a transaction. + * fifo_icap_busy - Check the ICAP busy status. * @drvdata: a pointer to the drvdata. + * + * Returns: %true if the ICAP is still processing a transaction, + * otherwise %false **/ static inline u32 fifo_icap_busy(struct hwicap_drvdata *drvdata) { @@ -178,7 +185,7 @@ static inline u32 fifo_icap_busy(struct hwicap_drvdata *drvdata) * fifo_icap_write_fifo_vacancy - Query the write fifo available space. * @drvdata: a pointer to the drvdata. * - * Return the number of words that can be safely pushed into the write fifo. + * Returns: the number of words that can be safely pushed into the write fifo. **/ static inline u32 fifo_icap_write_fifo_vacancy( struct hwicap_drvdata *drvdata) @@ -190,7 +197,7 @@ static inline u32 fifo_icap_write_fifo_vacancy( * fifo_icap_read_fifo_occupancy - Query the read fifo available data. * @drvdata: a pointer to the drvdata. * - * Return the number of words that can be safely read from the read fifo. + * Returns: the number of words that can be safely read from the read fifo. **/ static inline u32 fifo_icap_read_fifo_occupancy( struct hwicap_drvdata *drvdata) @@ -205,10 +212,12 @@ static inline u32 fifo_icap_read_fifo_occupancy( * ICAP device. * @num_words: the number of words (32 bit) to write to the ICAP * device. - + * * This function writes the given user data to the Write FIFO in * polled mode and starts the transfer of the data to * the ICAP device. + * + * Returns: %0 on success or %-errno on failure. **/ int fifo_icap_set_configuration(struct hwicap_drvdata *drvdata, u32 *frame_buffer, u32 num_words) @@ -280,11 +289,13 @@ int fifo_icap_set_configuration(struct hwicap_drvdata *drvdata, /** * fifo_icap_get_configuration - Read configuration data from the device. * @drvdata: a pointer to the drvdata. - * @data: Address of the data representing the partial bitstream - * @size: the size of the partial bitstream in 32 bit words. + * @frame_buffer: Address of the data representing the partial bitstream + * @num_words: the size of the partial bitstream in 32 bit words. * * This function reads the specified number of words from the ICAP device in * the polled mode. + * + * Returns: %0 on success or %-errno on failure. */ int fifo_icap_get_configuration(struct hwicap_drvdata *drvdata, u32 *frame_buffer, u32 num_words) @@ -347,7 +358,7 @@ int fifo_icap_get_configuration(struct hwicap_drvdata *drvdata, } /** - * buffer_icap_reset - Reset the logic of the icap device. + * fifo_icap_reset - Reset the logic of the icap device. * @drvdata: a pointer to the drvdata. * * This function forces the software reset of the complete HWICAP device. diff --git a/drivers/char/xillybus/xillybus_class.c b/drivers/char/xillybus/xillybus_class.c index c92a628e389e..5e8f03b77064 100644 --- a/drivers/char/xillybus/xillybus_class.c +++ b/drivers/char/xillybus/xillybus_class.c @@ -57,7 +57,7 @@ int xillybus_init_chrdev(struct device *dev, size_t namelen; struct xilly_unit *unit, *u; - unit = kzalloc(sizeof(*unit), GFP_KERNEL); + unit = kzalloc_obj(*unit); if (!unit) return -ENOMEM; diff --git a/drivers/char/xillybus/xillybus_core.c b/drivers/char/xillybus/xillybus_core.c index 11b7c4749274..952ef149aba1 100644 --- a/drivers/char/xillybus/xillybus_core.c +++ b/drivers/char/xillybus/xillybus_core.c @@ -319,7 +319,7 @@ static int xilly_map_single(struct xilly_endpoint *ep, dma_addr_t addr; struct xilly_mapping *this; - this = kzalloc(sizeof(*this), GFP_KERNEL); + this = kzalloc_obj(*this); if (!this) return -ENOMEM; @@ -1184,8 +1184,7 @@ static int xillybus_flush(struct file *filp, fl_owner_t id) static void xillybus_autoflush(struct work_struct *work) { - struct delayed_work *workitem = container_of( - work, struct delayed_work, work); + struct delayed_work *workitem = to_delayed_work(work); struct xilly_channel *channel = container_of( workitem, struct xilly_channel, rd_workitem); int rc; @@ -1974,7 +1973,7 @@ EXPORT_SYMBOL(xillybus_endpoint_remove); static int __init xillybus_init(void) { - xillybus_wq = alloc_workqueue(xillyname, 0, 0); + xillybus_wq = alloc_workqueue(xillyname, WQ_UNBOUND, 0); if (!xillybus_wq) return -ENOMEM; diff --git a/drivers/char/xillybus/xillyusb.c b/drivers/char/xillybus/xillyusb.c index 45771b1a3716..34e7ad3bcab3 100644 --- a/drivers/char/xillybus/xillyusb.c +++ b/drivers/char/xillybus/xillyusb.c @@ -495,7 +495,7 @@ static struct xillyusb_endpoint struct xillyusb_endpoint *ep; - ep = kzalloc(sizeof(*ep), GFP_KERNEL); + ep = kzalloc_obj(*ep); if (!ep) return NULL; @@ -522,7 +522,7 @@ static struct xillyusb_endpoint struct xillybuffer *xb; unsigned long addr; - xb = kzalloc(sizeof(*xb), GFP_KERNEL); + xb = kzalloc_obj(*xb); if (!xb) { endpoint_dealloc(ep); @@ -1336,7 +1336,7 @@ static int xillyusb_open(struct inode *inode, struct file *filp) } if (filp->f_mode & FMODE_READ) { - in_fifo = kzalloc(sizeof(*in_fifo), GFP_KERNEL); + in_fifo = kzalloc_obj(*in_fifo); if (!in_fifo) { rc = -ENOMEM; @@ -1943,7 +1943,7 @@ static int setup_channels(struct xillyusb_dev *xdev, struct xillyusb_channel *chan, *new_channels; int i; - chan = kcalloc(num_channels, sizeof(*chan), GFP_KERNEL); + chan = kzalloc_objs(*chan, num_channels); if (!chan) return -ENOMEM; @@ -2149,7 +2149,7 @@ static int xillyusb_probe(struct usb_interface *interface, struct xillyusb_dev *xdev; int rc; - xdev = kzalloc(sizeof(*xdev), GFP_KERNEL); + xdev = kzalloc_obj(*xdev); if (!xdev) return -ENOMEM; @@ -2163,7 +2163,7 @@ static int xillyusb_probe(struct usb_interface *interface, spin_lock_init(&xdev->error_lock); xdev->in_counter = 0; xdev->in_bytes_left = 0; - xdev->workq = alloc_workqueue(xillyname, WQ_HIGHPRI, 0); + xdev->workq = alloc_workqueue(xillyname, WQ_HIGHPRI | WQ_UNBOUND, 0); if (!xdev->workq) { dev_err(&interface->dev, "Failed to allocate work queue\n"); @@ -2275,7 +2275,7 @@ static int __init xillyusb_init(void) { int rc = 0; - wakeup_wq = alloc_workqueue(xillyname, 0, 0); + wakeup_wq = alloc_workqueue(xillyname, WQ_UNBOUND, 0); if (!wakeup_wq) return -ENOMEM; |
